Imports WRDB.Project

Public Class frmGIS

#Region "Declarations"

    'most recently used file list...
    Dim mru As clsMruStripMenu

    Private Structure structAnimate
        Dim dsData As WRDB.DataSource.clsData 'for animation results
        Dim DataLayer, DataField As String 'mapping to polygon shapefile
        Dim StationList As Generic.List(Of String) 'list of data stations IDs found in data file
        Dim DataHandle As Integer
        Dim MinDate, MaxDate, ThisDate, NextDate As Date
        Dim MinValue, MaxValue As Double
        Dim MinNum, MaxNum As Integer
        Dim TimeStep As Double 'time step in days
        Dim TimeUnits As enumTimeUnits
        Dim Speed As Integer 'relative speed from 1 to 5 (1 is slowest)
        Dim Colors() As Color '5 colors for below threshold value, and 0, 1/3, 2/3, and 1 intensities
        Dim InvertColor As Boolean
        Dim ThresholdType As enumThreshold
        Dim ThresholdValue As Double
    End Structure

    Friend Enum enumThreshold
        None
        ColorAbove
        ColorBelow
    End Enum

    Private Enum enumDisplay
        None
        NumResult
        MinResult
        MaxResult
        AvgResult
    End Enum

    Private Enum enumAggregate
        POR
        None
        Month
        Growing
        Annual
    End Enum

    Private Enum enumField
        ID
        Name
        Type
        Freq
        Lat
        Lon
        Elev
        Branch
        Reach
        RMile
        Area
        Agency
        RefID
        Remarks
        Other
        HUC
    End Enum

    ''' <summary>
    ''' Controls how data are displayed in GIS form
    ''' </summary>
    Public Enum enumMode
        ''' <summary>Display all stations from support table</summary>
        Display

        ''' <summary>Select from all stations found in browser</summary>
        [Select]

        ''' <summary>Display summary from last query</summary>
        Output

        ''' <summary>Allow data files to be loaded and associated with polygon shapefile; disables display of WRDB Stations data</summary>
        Animate

        ''' <summary>Allow selection of lat/long rectangle</summary>
        AreaOfInterest

        ''' <summary>Display all stations supplied in tab-separated list like Station ID/Name(tab)Latitude(tab)Longitude</summary>
        Stations
    End Enum

    Private Structure structMode
        Dim LastSettingsFilename As String
        Dim DataItemsVisible As Boolean
        Dim AllowAnimation As Boolean
        Dim AllowStationSelection As Boolean
        Dim DisplayCoordsByType As Boolean
        Dim SelectStationsFromDataTable As Boolean
        Dim DataTableName As String
        Sub Initialize(ByVal WRDBShapeDirectory As String, ByVal Mode As enumMode)
            LastSettingsFilename = WRDBShapeDirectory & IIf(Mode = enumMode.Animate, "\LastAnim.GIS", "\LastGIS.GIS")
            DataItemsVisible = (Mode = enumMode.Animate)
            AllowAnimation = (Mode = enumMode.Animate Or Mode = enumMode.Output)
            AllowStationSelection = (Mode = enumMode.Select Or Mode = enumMode.Stations)
            DisplayCoordsByType = (Mode = enumMode.Display Or Mode = enumMode.Select)
            If Mode = enumMode.Select Then DataTableName = WRDBProject.FullTableName(ActiveTableName) Else DataTableName = TempData
            SelectStationsFromDataTable = (Mode = enumMode.Output OrElse (Mode = enumMode.Select AndAlso Not WRDBProject.GisQuickTable))
        End Sub
    End Structure

    Private Enum enumTimeUnits
        Minute
        Hour
        Day
        Month
        Year
    End Enum

    ''' <summary>
    ''' Temporary assignments for growing season limits (later allow to be user-defined)
    ''' </summary>
    Const GrowingMonth1 = 3, GrowingDay1 = 15, GrowingMonth2 = 11, GrowingDay2 = 15

    Private ActiveLayer As clsLayer

    Private Aggregate As enumAggregate

    Private AnimationInfo As structAnimate

    ''' <summary>Data table containing summary information for each station id and each aggregate timestep in data file attached to shape file used for animation</summary>
    Private dtPolygon As DataTable

    ''' <summary>
    ''' Data table containing summary information for each WRDB station
    ''' </summary>
    Private dtResults As DataTable

    ''' <summary>Name of last .GIS file loaded or saved as</summary>
    Private GISFileName As String = ""

    ''' <summary>Subclassed MapWinGIS control that allows me to handle mouse wheel zooms</summary>
    Friend WithEvents GISMap As New MouseWheelMap

    ''' <summary>Flag set whenever anything stored in .GIS file changed by user</summary>
    Private IsModified As Boolean = False

    ''' <summary>Flag indicating whether stations are being selected using crossing box</summary>
    Private IsSelecting As Boolean = False

    ''' <summary>Cached pixel coordinates of each WRDB stations; to speed up tooltip display when hovering over stations</summary>
    Private lstCoords As New Generic.List(Of Point)

    ''' <summary>List of selected stations</summary>
    Private lstStations As New Generic.List(Of String)

    ''' <summary>The form mode, controlling how data are displayed</summary>
    Friend Mode As enumMode

    ''' <summary>Numerous settings that control form based on mode value</summary>
    Private ModeSettings As structMode

    ''' <summary>Handle of WRDBShapefile</summary>
    Private WRDBLayerHandle As Integer

    ''' <summary>Temporary directory where WRDB Stations shapefile is saved</summary>
    Private WRDBShapeDirectory, WRDBShapeFileName As String

    ''' <summary>Name of WRDB Station layer</summary>
    Private WRDBLayerName As String = "WRDB Stations"

    ''' <summary>Name of WRDB Stations shapefile created dynamically</summary>
    Private WRDBShapefile As MapWinGIS.Shapefile

    ''' <summary>If true, animation parameters will not be updated (used when multiple comboboxes need to be updated during LoadSettings)</summary>
    Private SuspendMapUpdate As Boolean

    ''' <summary>Handle of drawing layer for AOI rectangle</summary>
    Private AOIDrawingHandle As Integer = -1

    ''' <summary>Coordinates of AOI rectangle</summary>
    Private _AOIRectangle As RectangleF

    ''' <summary>Set this true if want last graph to be loaded once form is loaded</summary>
    Public LoadLast As Boolean = False

    ''' <summary>Set this to full path of file if want it to be loaded once form is loaded</summary>
    Public LoadFile As String = ""

    ''' <summary>Set to true when form is closed so can possibly interrupt long-running load process</summary>
    Private IsCancelled As Boolean = False

#End Region

#Region "Drag-drop support for layer list"


    Private IsDragging As Boolean = False
    Private itemIndexDrag, itemIndexDrop As Integer

    'following needs work--probably not worth it!

    'Private Sub lstLayers_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragEnter
    '    If e.Data.GetDataPresent(DataFormats.StringFormat) And e.AllowedEffect = DragDropEffects.Move Then e.Effect = DragDropEffects.Move
    'End Sub

    'Private Sub lstLayers_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragOver
    '    itemIndexDrop = lstLayers.IndexFromPoint(lstLayers.PointToClient(New Point(e.X, e.Y)))
    '    IsDragging = True
    '    lstLayers.SelectedIndex = itemIndexDrop
    'End Sub

    'Private Sub lstLayers_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragDrop
    '    If e.Data.GetDataPresent(DataFormats.StringFormat) Then
    '        If itemIndexDrop >= 0 And itemIndexDrop < lstLayers.Items.Count Then
    '            lstLayers.Items.Insert(itemIndexDrop, e.Data.GetData(DataFormats.Text))
    '        Else 'add to bottom of list
    '            lstLayers.Items.Add(e.Data.GetData(DataFormats.Text))
    '        End If
    '        IsDragging = False
    '    End If
    'End Sub

    'Private Sub lstLayers_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lstLayers.MouseDown
    '    itemIndexDrag = lstLayers.IndexFromPoint(e.X, e.Y)
    '    If itemIndexDrag >= 0 And itemIndexDrag < lstLayers.Items.Count Then
    '        lstLayers.DoDragDrop(lstLayers.Items(itemIndexDrag), DragDropEffects.Move)
    '    End If
    'End Sub

    Private Sub lstLayers_ItemCheck(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles lstLayers.ItemCheck
        If ActiveLayer IsNot Nothing Then ActiveLayer.Visible = (e.NewValue = CheckState.Checked)
    End Sub

    Private Sub lstLayers_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles lstLayers.SelectedIndexChanged
        With lstLayers
            If Not IsDragging Then ActiveLayer = clsLayer.Reset(GISMap, .Items.Count - 1 - .SelectedIndex)
        End With
        pgLayers.SelectedObject = ActiveLayer
    End Sub
#End Region

#Region "Form events"


    ''' <summary>
    ''' Instantiate new GIS form and initialize (will always load last GIS file)
    ''' </summary>
    ''' <param name="_WRDBProject">WRDB Project (to get default settings, etc.)</param>
    ''' <param name="_TableName">Currently active data table (Select mode)</param>
    ''' <param name="_TempData">Name of temp table (Display mode)</param>
    ''' <param name="_Mode">GIS display mode</param>
    ''' <param name="_WRDBStationTypes">List of available station types to speed processing</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal _WRDBProject As clsProject, ByVal _TableName As String, ByVal _TempData As String, ByVal _Mode As frmGIS.enumMode, ByVal _WRDBStationTypes As Generic.List(Of String))

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        WRDBProject = _WRDBProject
        ActiveTableName = _TableName
        TempData = _TempData
        WRDBStationTypes = _WRDBStationTypes
        GISForm = Me
        LoadLast = True
        Mode = _Mode
        'ModeSettings.Initialize(Mode)
    End Sub

    ''' <summary>
    ''' Instantiate new GIS form and initialize
    ''' </summary>
    ''' <param name="GisFile">GIS file to load</param>
    ''' <param name="LoadLast">Flag indicating if last map should be loaded</param>
    ''' <remarks></remarks>
    Public Sub New(Optional ByVal GisFile As String = "", Optional ByVal LoadLast As Boolean = False)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.LoadFile = GisFile
        Me.LoadLast = LoadLast
        WRDBProject = Nothing
        ActiveTableName = ""
        TempData = ""
        GISForm = Me
        WRDBStationTypes.Clear()
        Mode = enumMode.Animate
        'ModeSettings.Initialize(Mode)
    End Sub

    ''' <summary>
    ''' Instantiate new GIS form and initialize
    ''' </summary>
    ''' <param name="_Mode">GIS display mode</param>
    ''' <param name="_StationList">Tab-separated stations list like Station ID/Name(tab)Latitude(tab)Longitude</param>
    Public Sub New(ByVal _Mode As frmGIS.enumMode, Optional ByVal _StationList As Generic.List(Of String) = Nothing)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.LoadFile = ""
        Me.LoadLast = True
        WRDBProject = Nothing
        ActiveTableName = ""
        TempData = ""
        GISForm = Me
        StationList = _StationList
        WRDBStationTypes.Clear()
        Mode = _Mode
        'ModeSettings.Initialize(Mode)
    End Sub

    Private Sub CheckDropDownItem(ByVal ParentMenuItem As Object, ByVal Index As Integer)
        For i As Integer = 0 To ParentMenuItem.DropDownItems.Count - 1
            CType(ParentMenuItem.DropDownItems(i), ToolStripMenuItem).Checked = i = Index
        Next
    End Sub

    Private Sub CheckDropDownItem(ByVal ParentMenuItem As ToolStripMenuItem, ByVal DropDownItem As ToolStripMenuItem)
        For i As Integer = 0 To ParentMenuItem.DropDownItems.Count - 1
            CType(ParentMenuItem.DropDownItems(i), ToolStripMenuItem).Checked = ParentMenuItem.DropDownItems(i) Is DropDownItem
        Next
    End Sub

    Private Sub frmGIS_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        Try
            ' Call sub to save the MRU filenames
            mru.SaveToRegistry()

            If Not My.Computer.FileSystem.DirectoryExists(TempPath) Then My.Computer.FileSystem.CreateDirectory(TempPath)
            SaveSettings(ModeSettings.LastSettingsFilename)
            If dtResults IsNot Nothing Then dtResults.Dispose()
            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
            SaveWindowPos(REGAPP, Me)
            SaveSetting(REGAPP, Me.Name, "SplitMainCollapsed", splitMain.IsCollapsed)
            SaveSetting(REGAPP, Me.Name, "SplitMainDistance", splitMain.ExpandedDistance)
            SaveSetting(REGAPP, Me.Name, "SplitLayersDistance", splitLayers.SplitterDistance)
            SaveSetting(REGAPP, Me.Name, "SplitInfoDistance", splitInfo.SplitterDistance)
            SaveSetting(REGAPP, Me.Name, "Aggregate", Aggregate)
            'GISForm.Dispose()
            GISForm = Nothing
            If WRDBStationTypes IsNot Nothing Then WRDBStationTypes.Clear()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub frmGIS_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        Try
            If IsModified And GISMap.NumLayers > 0 AndAlso Not (GISMap.NumLayers = 1 AndAlso WRDBShapefile IsNot Nothing) Then
                Select Case SaveChangesWarning(GISFileName)
                    Case Windows.Forms.DialogResult.Yes : e.Cancel = Not SaveSettings(GISFileName)
                    Case Windows.Forms.DialogResult.No
                    Case Windows.Forms.DialogResult.Cancel : e.Cancel = True
                End Select
            End If
            If Not e.Cancel Then IsCancelled = True : Application.DoEvents()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub frmGIS_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            'If MainForm IsNot Nothing Then MainForm.WriteProgress("Preparing GIS form...")
            WriteProgress("Preparing GIS form...")
            GetWindowPos(REGAPP, Me)
            splitMain.IsCollapsed = GetSetting(REGAPP, Me.Name, "SplitMainCollapsed", False)
            splitMain.ExpandedDistance = GetSetting(REGAPP, Me.Name, "SplitMainDistance", 200)
            ''must do this below--it is not working properly!!!!! they keep shrinking!
            'Try
            '    splitInfo.SplitterDistance = GetSetting(REGAPP, Me.Name, "SplitInfoDistance", 200)
            '    splitLayers.SplitterDistance = GetSetting(REGAPP, Me.Name, "SplitLayersDistance", 400)
            '    'Debug.Print(splitInfo.SplitterDistance & "," & splitLayers.SplitterDistance)
            'Catch ex As Exception
            'End Try
            Aggregate = GetSetting(REGAPP, Me.Name, "Aggregate", enumAggregate.POR)

            'initialize mru (most recently used) menu list

            mru = New clsMruStripMenu(mnuFileRecent, AddressOf RecentFileHandler, "Software\VB and VBA Program Settings\WRDBGRAPH\GIS Files", 9)
            With mru
                .MaxShortenPathLength = 30
                .LoadFromRegistry()

                'make sure all these mru files exist
                For i As Integer = .NumEntries - 1 To 0 Step -1
                    If Not My.Computer.FileSystem.FileExists(.GetFileAt(i)) Then .RemoveFile(i)
                Next
                .SaveToRegistry()
            End With

            Try
                Me.splitMain.Panel2.Controls.Add(GISMap)
            Catch ex As Exception
                ErrorMsg("Unable to display GIS form because the MapWindow control has not been installed. Please download and install it from the WRDB web site.", ex)
                Close()
                Exit Sub
            End Try

            With GISMap
                .Dock = DockStyle.Fill
                .SendMouseMove = True
                .SendSelectBoxFinal = True
                .SendSelectBoxDrag = True
                .SendMouseDown = True
                .SendMouseUp = False
                .SendOnDrawBackBuffer = False
            End With
            GISForm = Me
            ProjectProjection = ""

            WRDBShapeDirectory = TempPath

            'if project's GIS directory has been set and exists, use it instead of temporary directory

            If WRDBProject IsNot Nothing AndAlso WRDBProject.GisDirectory <> "" AndAlso My.Computer.FileSystem.DirectoryExists(WRDBProject.GisDirectory) Then WRDBShapeDirectory = WRDBProject.GisDirectory

            WRDBShapeFileName = String.Format("{0}\{1}.shp", WRDBShapeDirectory, WRDBLayerName)

            For i As Integer = 0 To DefaultLayerColor.Length - 1
                DefaultLayerColor(i) = Choose((i Mod 14) + 1, Color.Red, Color.Blue, Color.Green, Color.Cyan, Color.Magenta, Color.DarkOrange, Color.Gray, Color.Black, Color.DarkRed, Color.DarkBlue, Color.DarkGreen, Color.DarkCyan, Color.DarkMagenta, Color.Brown)
            Next

            With AnimationInfo
                ReDim .Colors(4)
                .Colors(0) = Color.LightGray
                .Colors(1) = Color.Green
                .Colors(2) = Color.Yellow
                .Colors(3) = Color.Red
                .Colors(4) = Color.White
                .ThresholdType = enumThreshold.None
                .ThresholdValue = 0.0
                .InvertColor = False
                .Speed = 3
            End With

            IsModified = False

            ModeSettings.Initialize(WRDBShapeDirectory, Mode)

            'control visibility of data items
            If Not ModeSettings.DataItemsVisible Then
                mnuFileSepData.Visible = False
                mnuFileOpen.Visible = False
                mnuFileOpenTable.Visible = False
                mnuFileClose.Visible = False
                mnuViewData.Visible = False
                mnuViewLegend.Visible = False
                mnuDataDateTime.Visible = False
                mnuDataSepOptions.Visible = False
                mnuDataOptions.Visible = False
                tbSepData.Visible = False
                tbOpen.Visible = False
                tbClose.Visible = False
            End If

            If ModeSettings.AllowAnimation Then
                lblLocation.Visible = False
                txtLocation.Visible = False
                btnLocation.Visible = False
                sepLocation.Visible = False
                mnuViewAnimate.Enabled = False
                mnuViewAnimateSpeed.Enabled = False
                tbAnim.Visible = False
            Else
                mnuFileExport.Visible = False
                mnuFileSepAnim.Visible = False
                mnuFileMovie.Visible = False
                mnuViewSepAnimate.Visible = False
                mnuViewAnimate.Visible = False
                mnuViewAnimateSpeed.Visible = False
                mnuDataDateTime.Visible = False
                mnuDataAggregate.Visible = False
                mnuDataPCode.Visible = False
                trkAnim.Visible = False
                tbAnim.Visible = False
                cboPCode.Visible = False
                cboAggregate.Visible = False
            End If

            tbMain.Refresh()
            Application.DoEvents()

            frmToolbar.GetToolbarSettings(tbMain)

            GISMap.Visible = False
            SuspendMapUpdate = True

            'control contents of toolbar combos and submenus
            Select Case Mode
                Case enumMode.Output, enumMode.Animate
                    cboDisplay.Items.AddRange(New String() {"No. of Observations", "Minimum Values", "Maximum Values", "Average Values"})
                    cboDisplay.SelectedIndex = 0
                    With mnuDataDisplay.DropDownItems
                        .Add("No. of Observations")
                        .Add("Minimum Values")
                        .Add("Maximum Values")
                        .Add("Average Values")
                    End With
                    CheckDropDownItem(mnuDataDisplay, 0)

                    If Mode = enumMode.Output Then
                        For Each s As String In WRDBProject.DB.GetRecordList(IIf(WRDBProject.GisQuickList, "PCodes", TempData), "PCODE")
                            cboPCode.Items.Add(s)
                            mnuDataPCode.DropDownItems.Add(s)
                        Next
                    End If
                    If cboPCode.Items.Count > 0 Then
                        cboPCode.SelectedIndex = 0
                        CheckDropDownItem(mnuDataPCode, 0)
                    End If

                    CheckDropDownItem(mnuDataAggregate, Aggregate)
                    tbAnim.Visible = Aggregate <> enumAggregate.POR
                    cboAggregate.SelectedIndex = 0

                    With AnimationInfo
                        CheckDropDownItem(mnuViewAnimateSpeed, .Speed - 1)
                        CheckDropDownItem(tbAnimSpeed, .Speed - 1)
                    End With

                Case enumMode.Display
                    WRDBProject.ResetPDox("Stations")
                    cboDisplay.Items.Add("All Stations")
                    mnuDataDisplay.DropDownItems.Add("All Stations")
                    For Each s As String In WRDBProject.DB.GetRecordList("Stations", "Station_Type")
                        cboDisplay.Items.Add(s)
                        mnuDataDisplay.DropDownItems.Add(s)
                    Next
                    cboDisplay.SelectedIndex = 0
                    CheckDropDownItem(mnuDataDisplay, 0)

                Case enumMode.Select, enumMode.Stations
                    cboDisplay.Items.Add("All Stations")
                    mnuDataDisplay.DropDownItems.Add("All Stations")
                    If WRDBStationTypes.Count = 0 Then
                        Dim SQL As String
                        If Mode = enumMode.Stations Then
                            For Each s As String In StationList
                                Dim StationType As String = s.Split(vbTab)(2)
                                If StationType <> "" And Not cboDisplay.Items.Contains(StationType) Then
                                    cboDisplay.Items.Add(StationType)
                                    mnuDataDisplay.DropDownItems.Add(StationType)
                                End If
                            Next
                        Else
                            WRDBProject.ResetPDox("Stations")
                            If WRDBProject.GisQuickList Then
                                SQL = String.Format("SELECT DISTINCT Station_Type FROM STATIONS", WRDBProject.FullTableName(ActiveTableName))
                            Else
                                SQL = String.Format("SELECT DISTINCT Station_Type FROM STATIONS,{0} WHERE STATIONS.STATION_ID={0}.STATION_ID", WRDBProject.FullTableName(ActiveTableName))
                            End If
                            Dim dr As DbDataReader = WRDBProject.DB.ExecuteReader(SQL)
                            While dr IsNot Nothing AndAlso dr.Read
                                If Not dr.IsDBNull(0) Then
                                    cboDisplay.Items.Add(dr.GetString(0))
                                    mnuDataDisplay.DropDownItems.Add(dr.GetString(0))
                                End If
                            End While
                            dr.Close()
                        End If
                        With WRDBStationTypes
                            .Clear()
                            For Each s As String In cboDisplay.Items
                                .Add(s)
                            Next
                        End With
                    Else
                        For Each s As String In WRDBStationTypes
                            cboDisplay.Items.Add(s)
                            mnuDataDisplay.DropDownItems.Add(s)
                        Next
                    End If
                    cboDisplay.SelectedIndex = 0
                    CheckDropDownItem(mnuDataDisplay, 0)

                Case enumMode.AreaOfInterest
                    mnuDataDisplay.Visible = False

            End Select

            cboDisplay.MaxDropDownItems = Math.Min(25, Math.Max(1, cboDisplay.Items.Count))
            cboPCode.MaxDropDownItems = 15

            SuspendMapUpdate = False

            If LoadLast Then mnuFileOpenLast.PerformClick()

            Select Case Mode
                Case enumMode.Display, enumMode.Select
                    CreateStationsLayer()
                Case enumMode.Output
                    CreateStationsLayer()
                    SummarizeStationData()
                Case enumMode.Animate
                Case enumMode.Stations
                    CreateStationsLayer(StationList)
                Case enumMode.AreaOfInterest
            End Select

            If lstLayers.Items.Count > 0 Then lstLayers.SelectedIndex = 0
            Me.pgLayers.SelectedObject = ActiveLayer

            'If MainForm IsNot Nothing Then MainForm.WriteProgress("Summarizing data...")
            WriteProgress("Summarizing data...")

            If Not LoadLast Then
                If LoadFile <> "" AndAlso My.Computer.FileSystem.FileExists(LoadFile) Then
                    LoadSettings(LoadFile)
                Else
                    cboAggregate.SelectedIndex = 0
                End If
                ResetAnimation()
                ResetTrackbar()
                RefreshDisplay()
            End If

            IsModified = False

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
            ProgressComplete()
            GISMap.Visible = True
        End Try
    End Sub

    Private Sub RecentFileHandler(ByVal Number As Integer, ByVal FileName As String)
        LoadSettings(FileName)
        If Mode = enumMode.Animate AndAlso AnimationInfo.dsData IsNot Nothing Then SummarizePolygonData()

        ResetAnimation()
        ResetTrackbar()
        RefreshDisplay()

        ' Call sub to reorder the MRU list
        mru.AddFile(FileName)
        mru.SaveToRegistry()
        GroupFileName = FileName
        ResetTitleBar()
    End Sub

#End Region

#Region "Map Color Functions..."

    ''' <summary>
    ''' Return gradient color using 4-color transition
    ''' </summary>
    ''' <param name="Intensity">Number from 0.0 to 1.0 indicating desired relative value</param>
    ''' <param name="_Colors">Array of 5 colors associated with below threshold and smallest intensity to largest</param>
    ''' <param name="InvertColors">If true, swap order of colors</param>
    ''' <param name="ThresholdType">How thresholds are handled</param>
    ''' <param name="ThresholdValue">If type is not none, is threshold intensity</param>
    Friend Shared Function GradientColor(ByVal Intensity As Single, ByVal _Colors() As Color, ByVal InvertColors As Boolean, ByVal ThresholdType As enumThreshold, ByVal ThresholdValue As Double) As Color
        Dim Colors() As Color = _Colors.Clone
        If InvertColors Then Array.Reverse(Colors, 1, 4) 'reverse the top 4 colors (not the non-threshold value)
        If Intensity < 0.33 Then
            GradientColor = ColorCombine(Colors(1), Colors(2), (Intensity - 0.0) / 0.33)
        ElseIf Intensity < 0.67 Then
            GradientColor = ColorCombine(Colors(2), Colors(3), (Intensity - 0.33) / 0.33)
        Else
            GradientColor = ColorCombine(Colors(3), Colors(4), (Intensity - 0.67) / 0.33)
        End If
        Select Case ThresholdType
            Case enumThreshold.None
            Case enumThreshold.ColorAbove
                If Intensity < ThresholdValue Then GradientColor = Colors(0)
            Case enumThreshold.ColorBelow
                If Intensity > ThresholdValue Then GradientColor = Colors(0)
        End Select
    End Function

    ''' <summary>
    ''' Return color associated with intensity using Animation defaults
    ''' </summary>
    ''' <param name="Intensity">Number from 0.0 to 1.0 indicating desired relative value</param>
    Friend Function GradientColor(ByVal Intensity As Single) As Color
        With AnimationInfo
            Return frmGIS.GradientColor(Intensity, .Colors, .InvertColor, .ThresholdType, (.ThresholdValue - .MinValue) / (.MaxValue - .MinValue))
        End With
    End Function

    Private Shared Function ColorCombine(ByVal Color1 As System.Drawing.Color, ByVal Color2 As System.Drawing.Color, ByVal Opacity As Double) As System.Drawing.Color
        Opacity = Math.Min(1, Math.Max(0, Opacity))
        Return Drawing.Color.FromArgb(GammaCombine(Color1.R, Color2.R, Opacity), _
                                        GammaCombine(Color1.G, Color2.G, Opacity), _
                                        GammaCombine(Color1.B, Color2.B, Opacity))
    End Function

    Private Shared Function GammaCombine(ByVal v1 As Integer, ByVal v2 As Integer, ByVal a As Double)
        Dim d1 As Double = v1 / 255.0
        Dim d2 As Double = v2 / 255.0
        Dim f As Double = 2.2
        Dim r As Integer = Int(Math.Pow(Math.Pow(d1, f) * (1 - a) + Math.Pow(d2, f) * a, 1 / f) * 255 + 0.5)
        Return Math.Min(255, r)
    End Function

    Private Function ColorToUInt(ByVal c As Color) As UInt32
        Return Convert.ToUInt32(System.Drawing.ColorTranslator.ToWin32(c))
    End Function

#End Region

    Private Sub CustomizeToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuCustomize.Click
        With New frmToolbar(tbMain)
            .ShowDialog(Me)
            .Dispose()
        End With
    End Sub

    ''' <summary>
    ''' Used to find the data row associated with the station that was just clicked on
    ''' </summary>
    ''' <param name="ScreenX">X Mouse click position in pixels</param>
    ''' <param name="ScreenY">Y Mouse click position in pixels</param>
    ''' <returns>Datarow containing station clicked on (or Nothing if didn't click close enough)</returns>
    Private Function FindShapes(ByVal ScreenX As Integer, ByVal ScreenY As Integer) As Generic.List(Of Integer)
        Dim lst As New Generic.List(Of Integer)
        Try
            Const WindowSize As Integer = 5

            With WRDBShapefile

                'following cache speeds up search--only computed when extents change
                If lstCoords.Count = 0 Then
                    For r As Integer = 0 To .NumShapes - 1
                        If IsCancelled Or .Shape(r) Is Nothing Then Return lst
                        With .Shape(r)
                            Dim x, y As Double
                            GISMap.ProjToPixel(.Point(0).x, .Point(0).y, x, y)
                            lstCoords.Add(New Point(x, y))
                        End With
                        If r Mod 1000 = 0 Then Application.DoEvents()
                    Next
                End If

                For r As Integer = 0 To Math.Min(lstCoords.Count, .NumShapes) - 1
                    Dim p As Point = lstCoords(r)
                    Dim typeMatches As Boolean = True
                    If ModeSettings.DisplayCoordsByType Then
                        Dim value As String = TestNull(.CellValue(enumField.Type, r), "")
                        typeMatches = (cboDisplay.Text = value OrElse (cboDisplay.SelectedIndex = 0 AndAlso cboDisplay.Items.Contains(value)))
                    End If
                    If typeMatches AndAlso Math.Abs(ScreenX - p.X) < WindowSize AndAlso Math.Abs(ScreenY - p.Y) < WindowSize Then lst.Add(r)
                Next
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
        Return lst
    End Function

    Private Function Invert(ByVal clr As Color) As Color
        Return Color.FromArgb(Not clr.R, Not clr.G, clr.B)
    End Function

    Private Function SaveSettings(ByRef _FileName As String, Optional ByVal SaveAs As Boolean = False) As Boolean
        Try
            If SaveAs Or _FileName = "" Then
                With New SaveFileDialog
                    .AddExtension = True
                    .CheckPathExists = True
                    .CreatePrompt = False
                    .DefaultExt = ".GIS"
                    .Filter = "WRDB GIS File (*.GIS)|*.GIS"
                    .FilterIndex = 0
                    .OverwritePrompt = True
                    .Title = "Save GIS File"
                    .ValidateNames = True
                    If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                        _FileName = .FileName
                        ResetTitleBar()
                        .Dispose()
                    Else
                        .Dispose()
                        Return False
                    End If
                End With
            End If

            'save general settings and last layers loaded

            Dim IniFile As New clsWRDBIniFile(_FileName)
            With IniFile
                .Create()
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, CStr(lstLayers.Items.Count))
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMin, CType(GISMap.Extents, MapWinGIS.Extents).xMin.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMax, CType(GISMap.Extents, MapWinGIS.Extents).xMax.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMin, CType(GISMap.Extents, MapWinGIS.Extents).yMin.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMax, CType(GISMap.Extents, MapWinGIS.Extents).yMax.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Display, cboDisplay.Text)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_PCode, cboPCode.Text)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Aggregate, Aggregate.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DateTime, AnimationInfo.ThisDate.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_AnimSpeed, AnimationInfo.Speed.ToString)

                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataLayer, AnimationInfo.DataLayer)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataField, AnimationInfo.DataField)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor0, AnimationInfo.Colors(0).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor1, AnimationInfo.Colors(1).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor2, AnimationInfo.Colors(2).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor3, AnimationInfo.Colors(3).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor4, AnimationInfo.Colors(4).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataInvert, AnimationInfo.InvertColor.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdType, AnimationInfo.ThresholdType.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdValue, AnimationInfo.ThresholdValue.ToString)

                Dim FileName As String = ""
                If AnimationInfo.dsData IsNot Nothing Then FileName = AnimationInfo.dsData.FileName
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFile, FileName)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFileRelative, GetRelativePath(IO.Path.GetDirectoryName(.FileName), FileName))

                For i As Integer = lstLayers.Items.Count - 1 To 0 Step -1
                    lstLayers.SelectedIndex = i
                    If ActiveLayer IsNot Nothing Then ActiveLayer.Save(IniFile, i)
                Next
                .Save()
            End With
            'Else
            'If IsModified AndAlso GISMap.NumLayers > 1 Then
            '    Select Case SaveChangesWarning(FileName)
            '        Case Windows.Forms.DialogResult.Yes
            '            'save last layers used
            '            Dim IniFile As New clsWRDBIniFile
            '            With IniFile
            '                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, CStr(lstLayers.Items.Count))
            '                For i As Integer = lstLayers.Items.Count - 1 To 0 Step -1
            '                    lstLayers.SelectedIndex = i
            '                    ActiveLayer.Save(IniFile, i)
            '                Next
            '                .SaveAndClose()
            '            End With
            '        Case Windows.Forms.DialogResult.No
            '        Case Windows.Forms.DialogResult.Cancel : Exit Function
            '    End Select
            'End If
            'End If
            IsModified = False
            Return True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Function

    ''' <summary>
    ''' List of selected station IDs
    ''' </summary>
    Public Function SelectedStations() As Generic.List(Of String)
        Return lstStations
    End Function

    ''' <summary>
    ''' Return rectangle for area of interest (nothing if not specified)
    ''' </summary>
    Public Function AOIRectangle() As RectangleF
        Return _AOIRectangle
    End Function

    Private Sub AddLayer()
        Try
            With New OpenFileDialog
                .InitialDirectory = GetSetting(REGAPP, Me.Name, "AddLayer", My.Computer.FileSystem.SpecialDirectories.MyDocuments & "WRDB")
                .Filter = "ArcView Shape Files (*.shp)|*.shp"
                .AddExtension = True
                .CheckFileExists = True
                .DefaultExt = ".shp"
                .Multiselect = True
                .Title = "Select GIS Layer(s) to Add"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    Dim text As String = ""
                    For Each s As String In .FileNames
                        Dim fn As String = ""
                        If WRDBShapefile IsNot Nothing Then
                            fn = WRDBShapefile.Filename
                            WRDBShapefile.Close() 'in case need to reproject when add new layer
                        End If
                        ActiveLayer = clsLayer.Add(GISMap, s)
                        If ActiveLayer IsNot Nothing Then
                            text = ActiveLayer.Name
                            ActiveLayer.MoveBelowPolylineLayers()
                            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Open(fn)
                            SaveSetting(REGAPP, Me.Name, "AddLayer", IO.Path.GetDirectoryName(s))
                        End If
                    Next
                    RefreshLayerList()
                    lstLayers.Text = text
                    If lstLayers.Items.Count = .FileNames.Length Then 'had no layer open before, so zoom extents
                        GISMap.ZoomToMaxExtents()
                    End If
                    .Dispose()
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub AnimTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles AnimTimer.Tick
        AnimTimer.Enabled = False
        IsCancelled = False
        With trkAnim
            If .Value = .Maximum Then
                .Value = .Minimum
            Else
                .Value = Math.Min(.Maximum, .Value + .SmallChange)
            End If
        End With
        If Not IsCancelled Then AnimTimer.Enabled = True
    End Sub

    Private Sub btnHelp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelp.Click
        mnuHelpContents.PerformClick()
    End Sub

    Private Sub btnLocation_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLocation.Click
        LocatePlace()
    End Sub

    Private Sub btnWhatsThis_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnWhatsThis.Click
        mnuHelpWhatsThis.PerformClick()
    End Sub

    Private Sub cbo_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboDisplay.SelectedIndexChanged, cboPCode.SelectedIndexChanged, cboAggregate.SelectedIndexChanged
        Try
            If SuspendMapUpdate Then Exit Sub
            IsCancelled = False
            CType(sender, ToolStripComboBox).Enabled = False 'so don't change again before summarize is done
            If sender Is cboDisplay Then
                CheckDropDownItem(mnuDataDisplay, cboDisplay.SelectedIndex)
            ElseIf sender Is cboAggregate Then
                tbAnim.Visible = cboAggregate.SelectedIndex <> 0
                mnuViewAnimate.Enabled = cboAggregate.SelectedIndex <> 0
                mnuViewAnimateSpeed.Enabled = cboAggregate.SelectedIndex <> 0

                CheckDropDownItem(mnuDataAggregate, cboAggregate.SelectedIndex)
                Aggregate = cboAggregate.SelectedIndex
                If Mode = enumMode.Output Then
                    SummarizeStationData()
                Else
                    ResetAnimation()
                End If
            Else
                If Mode = enumMode.Animate Then SummarizePolygonData() Else SummarizeStationData()
                CheckDropDownItem(mnuDataPCode, cboPCode.SelectedIndex)
            End If
            RefreshDisplay()
            ProgressComplete()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            CType(sender, ToolStripComboBox).Enabled = True
        End Try
    End Sub

    Friend Sub CopyMap(ByVal AxMap1 As AxMapWinGIS.AxMap)
        Try
            'Bitblasts a snapshot of the MapWindow map to the clipboard in VB Net
            Dim g1 As System.Drawing.Graphics = AxMap1.CreateGraphics()
            Dim MyImage As New System.Drawing.Bitmap(AxMap1.ClientRectangle.Width, AxMap1.ClientRectangle.Height, g1)
            Dim g2 As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(MyImage)
            Dim dc1 As IntPtr = g1.GetHdc()
            Dim dc2 As IntPtr = g2.GetHdc()
            Dim datobj As New System.Windows.Forms.DataObject
            BitBlt(dc2, 0, 0, AxMap1.ClientRectangle.Width, AxMap1.ClientRectangle.Height, dc1, 0, 0, 13369376)
            g1.ReleaseHdc(dc1)
            g2.ReleaseHdc(dc2)
            datobj.SetData(System.Windows.Forms.DataFormats.Bitmap, MyImage)
            System.Windows.Forms.Clipboard.SetDataObject(datobj)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Called when user has executed a query in WRDB and it has completed: update thematic map with results by recreating a shp file
    ''' </summary>
    Private Sub CreateStationsLayer() 'ByVal dtResults As System.Data.DataTable, ByVal Display As String)
        Try
            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
            WRDBShapefile = New MapWinGIS.Shapefile

            If Mode = enumMode.Display AndAlso WRDBProject.IsStationShapefileValid Then
                If WRDBShapefile.Open(WRDBShapeFileName) Then
                    ActiveLayer = clsLayer.Add(GISMap, WRDBShapeFileName)
                    If ActiveLayer Is Nothing Then Exit Sub
                    With CType(ActiveLayer, clsLayerPoint)
                        .Name = WRDBLayerName
                        .PointSize = 8
                        .PointType = MapWinGIS.tkPointType.ptTriangleDown
                        WRDBLayerHandle = .LayerHandle
                    End With
                    RefreshDisplay()
                    RefreshLayerList()
                    lstLayers.Text = ActiveLayer.Name
                    Exit Sub
                End If
            End If

            Try
                Dim dtStations As DataTable = Nothing
                Dim SQL As String = ""
                Dim fieldList As String = "Station_ID,Station_Name as Stat_Name,Station_Type as Stat_Type,Latitude,Longitude"
                If Not WRDBProject.GisQuickTable Then fieldList &= ",Data_Freq,Elevation,Branch_ID,Reach_ID,River_Mile,Dr_Area,Stations.Agency,Ref_ID,Remarks,Other,HUC"
                With WRDBProject
                    If ModeSettings.SelectStationsFromDataTable Then
                        SQL = String.Format("SELECT DISTINCT Dta.{0} FROM ({1} Dta LEFT OUTER JOIN STATIONS ON Dta.Station_ID=Stations.Station_ID) WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList, ModeSettings.DataTableName)
                        .ResetPDox(ActiveTableName)
                        .CopyPDoxSupportTables(.GetTableType(ActiveTableName), clsProject.enumTableTypes.Stations)
                    Else
                        SQL = String.Format("SELECT {0} FROM STATIONS WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList)
                        .ResetPDox(clsProject.enumTableTypes.Stations)
                    End If
                    'Select Case Mode
                    '    Case enumMode.Output
                    '        SQL = String.Format("SELECT DISTINCT Dta.{0} FROM ({1} Dta LEFT OUTER JOIN STATIONS ON Dta.Station_ID=Stations.Station_ID) WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList, ModeSettings.DataTableName)
                    '        .ResetPDox(ActiveTableName)
                    '        .CopyPDoxSupportTables(.GetTableType(ActiveTableName), clsProject.enumTableTypes.Stations)
                    '    Case enumMode.Select
                    '        If WRDBProject.GisQuickTable Then 'show all stations and avoid the join
                    '            SQL = String.Format("SELECT {0} FROM STATIONS WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList)
                    '            .ResetPDox(clsProject.enumTableTypes.Stations)
                    '        Else
                    '            SQL = String.Format("SELECT DISTINCT Dta.{0} FROM ({1} Dta LEFT OUTER JOIN STATIONS ON Dta.Station_ID=Stations.Station_ID) WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList, ModeSettings.DataTableName)
                    '            .ResetPDox(ActiveTableName)
                    '            .CopyPDoxSupportTables(.GetTableType(ActiveTableName), clsProject.enumTableTypes.Stations)
                    '        End If
                    '    Case enumMode.Display
                    '        SQL = String.Format("SELECT {0} FROM STATIONS WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList)
                    '        .ResetPDox(clsProject.enumTableTypes.Stations)
                    'End Select
                    WriteProgress("Loading data...")
                    If Not .DB.AddTable(dtStations, SQL) Then Exit Sub
                    ProgressComplete()
                End With
                dtStations.PrimaryKey = New DataColumn() {dtStations.Columns("Station_ID")}
                If dtStations.Rows.Count = 0 Then Exit Sub

                'first remove all existing WRDB stations layers

                With GISMap
                    For i As Integer = .NumLayers - 1 To 0 Step -1
                        Dim hdl As Integer = .get_LayerHandle(i)
                        If .get_LayerName(hdl) IsNot Nothing AndAlso .get_LayerName(hdl).Equals(WRDBLayerName, StringComparison.OrdinalIgnoreCase) Then .RemoveLayer(hdl)
                    Next
                End With

                'delete existing shp files and create and add new one

                Dim NumShapes As Integer = 0

                With WRDBShapefile
                    Dim fn As String = ""
                    Try
                        For i As Int16 = 1 To 5
                            Dim ext As String = Choose(i, "shp", "shx", "dbf", "prj", "mwsr")
                            fn = IO.Path.ChangeExtension(WRDBShapeFileName, ext)
                            If My.Computer.FileSystem.FileExists(fn) Then My.Computer.FileSystem.DeleteFile(fn)
                        Next
                    Catch ex As Exception
                        If My.Computer.FileSystem.FileExists(fn) Then
                            WarningMsg("Unable to delete file: {0}; it may be locked by another program.", fn)
                            Exit Sub
                        End If
                    End Try

                    If Not My.Computer.FileSystem.DirectoryExists(IO.Path.GetDirectoryName(WRDBShapeFileName)) Then My.Computer.FileSystem.CreateDirectory(IO.Path.GetDirectoryName(WRDBShapeFileName))

                    If .CreateNew(WRDBShapeFileName, MapWinGIS.ShpfileType.SHP_POINT) Then

                        .StartEditingShapes(True)
                        .StartEditingTable()

                        'add one field for each field in datatable
                        For i As Integer = 0 To IIf(WRDBProject.GisQuickTable, 4, dtStations.Columns.Count - 1)
                            Dim c As DataColumn = dtStations.Columns(i)
                            Dim fld As MapWinGIS.Field = New MapWinGIS.Field
                            fld.Name = c.ColumnName
                            If c.DataType Is System.Type.GetType("System.String") Then
                                fld.Type = MapWinGIS.FieldType.STRING_FIELD
                                fld.Width = 60
                            ElseIf c.DataType Is System.Type.GetType("System.Integer") Then
                                fld.Type = MapWinGIS.FieldType.INTEGER_FIELD
                            Else
                                fld.Type = MapWinGIS.FieldType.DOUBLE_FIELD
                                fld.Precision = 5
                            End If
                            .EditInsertField(fld, .NumFields + 1)
                        Next

                        Dim rownum As Integer = 0

                        'if showing all station locations, create normal shape file with all stations; will add layer of normal points colored by station type
                        'if showing thematic map of values, shape file will contain only stations with data; will draw symbols of varying sizes

                        Dim minx As Double = Double.MaxValue, maxx As Double = -minx

                        For Each r As DataRow In dtStations.Rows
                            Dim x As Single = -Math.Abs(TestNull(r.Item("Longitude"), 0))
                            Dim y As Single = TestNull(r.Item("Latitude"), 0)
                            If Math.Abs(x) > 1 And y > 1 And Math.Abs(x) < 180 And y < 90 Then
                                minx = Math.Min(minx, x)
                                maxx = Math.Max(maxx, x)
                                Dim pnt As New MapWinGIS.Point
                                pnt.x = x
                                pnt.y = y
                                Dim shp As New MapWinGIS.Shape
                                If shp.Create(MapWinGIS.ShpfileType.SHP_POINT) AndAlso shp.InsertPoint(pnt, 0) AndAlso .EditInsertShape(shp, .NumShapes) Then
                                    For colnum As Integer = 0 To IIf(WRDBProject.GisQuickTable, 4, dtStations.Columns.Count - 1)
                                        If Not IsDBNull(r.Item(colnum)) Then
                                            If dtStations.Columns(colnum).DataType Is GetType(String) Then
                                                .EditCellValue(colnum, .NumShapes - 1, CStr(r.Item(colnum)))
                                            ElseIf dtStations.Columns(colnum).DataType Is GetType(Integer) Then
                                                .EditCellValue(colnum, .NumShapes - 1, CInt(r.Item(colnum)))
                                            Else
                                                .EditCellValue(colnum, .NumShapes - 1, CDbl(r.Item(colnum)))
                                            End If
                                        End If
                                    Next
                                Else
                                    WarningMsg("Unable to create shape")
                                End If
                            Else
                            End If
                            rownum += 1
                            WriteProgress(rownum, dtStations.Rows.Count - 1, "Creating WRDB stations shape file...")
                        Next

                        WriteProgress("Saving shape file...")

                        .StopEditingTable(True)
                        .StopEditingShapes(True, True)
                        NumShapes = .NumShapes
                        .Projection = LatLongProjection
                        .SaveAs(WRDBShapeFileName)
                        .Close()

                        If NumShapes = 0 Then WarningMsg("No stations have been added to the WRDB Stations layer because none of the selected data items referenced stations with valid latitude and longitude.")

                        'need to reproject to the current setting for the MW project 

                        If NumShapes > 0 And ProjectProjection <> "" And Not GISMap.IsSameProjection(LatLongProjection, ProjectProjection) Then
                            Reproject(GISMap, WRDBShapeFileName, ProjectProjection, True)
                        End If
                    Else
                        ErrorMsg(GISMap.get_ErrorMsg(GISMap.LastErrorCode))
                    End If

                    'shape file is created, now add to view

                    ActiveLayer = clsLayer.Add(GISMap, WRDBShapeFileName)
                    If ActiveLayer Is Nothing Then Exit Sub
                    With CType(ActiveLayer, clsLayerPoint)
                        .Name = WRDBLayerName
                        .PointSize = 8
                        .PointType = MapWinGIS.tkPointType.ptTriangleDown
                        WRDBLayerHandle = .LayerHandle
                    End With

                    'reopen so is available elsewhere
                    WRDBShapefile.Open(WRDBShapeFileName)

                End With
                dtStations.Dispose()

            Catch ex As Exception
                ErrorMsg(, ex)
            Finally
                'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
                ProgressComplete()
            End Try
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Called when user has executed a query in WRDB and it has completed: update thematic map with results by recreating a shp file
    ''' </summary>
    Private Sub CreateStationsLayer(ByVal StationsList As Generic.List(Of String))
        Try
            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
            WRDBShapefile = New MapWinGIS.Shapefile

            Try
                Dim dtStations As New DataTable
                dtStations.Columns.Add("Station_ID", GetType(String))
                dtStations.Columns.Add("Station_Name", GetType(String))
                dtStations.Columns.Add("Station_Type", GetType(String))
                dtStations.Columns.Add("Latitude", GetType(Double))
                dtStations.Columns.Add("Longitude", GetType(Double))

                WriteProgress("Loading data...")
                For Each s As String In StationsList
                    dtStations.Rows.Add(s.Split(vbTab))
                    Application.DoEvents()
                Next
                ProgressComplete()
                dtStations.PrimaryKey = New DataColumn() {dtStations.Columns("Station_ID")}
                If dtStations.Rows.Count = 0 Then Exit Sub

                'first remove all existing WRDB stations layers

                With GISMap
                    For i As Integer = .NumLayers - 1 To 0 Step -1
                        Dim hdl As Integer = .get_LayerHandle(i)
                        If .get_LayerName(hdl) IsNot Nothing AndAlso .get_LayerName(hdl).Equals(WRDBLayerName, StringComparison.OrdinalIgnoreCase) Then .RemoveLayer(hdl)
                    Next
                End With

                'delete existing shp files and create and add new one

                Dim NumShapes As Integer = 0

                With WRDBShapefile
                    Dim fn As String = ""
                    Try
                        For i As Int16 = 1 To 5
                            Dim ext As String = Choose(i, "shp", "shx", "dbf", "prj", "mwsr")
                            fn = IO.Path.ChangeExtension(WRDBShapeFileName, ext)
                            If My.Computer.FileSystem.FileExists(fn) Then My.Computer.FileSystem.DeleteFile(fn)
                        Next
                    Catch ex As Exception
                        If My.Computer.FileSystem.FileExists(fn) Then
                            WarningMsg("Unable to delete file: {0}; it may be locked by another program.", fn)
                            Exit Sub
                        End If
                    End Try

                    If Not My.Computer.FileSystem.DirectoryExists(IO.Path.GetDirectoryName(WRDBShapeFileName)) Then My.Computer.FileSystem.CreateDirectory(IO.Path.GetDirectoryName(WRDBShapeFileName))

                    If .CreateNew(WRDBShapeFileName, MapWinGIS.ShpfileType.SHP_POINT) Then

                        .StartEditingShapes(True)
                        .StartEditingTable()

                        'add one field for each field in datatable
                        For i As Integer = 0 To dtStations.Columns.Count - 1
                            Dim c As DataColumn = dtStations.Columns(i)
                            Dim fld As MapWinGIS.Field = New MapWinGIS.Field
                            fld.Name = c.ColumnName
                            If c.DataType Is System.Type.GetType("System.String") Then
                                fld.Type = MapWinGIS.FieldType.STRING_FIELD
                                fld.Width = 60
                            ElseIf c.DataType Is System.Type.GetType("System.Integer") Then
                                fld.Type = MapWinGIS.FieldType.INTEGER_FIELD
                            Else
                                fld.Type = MapWinGIS.FieldType.DOUBLE_FIELD
                                fld.Precision = 5
                            End If
                            .EditInsertField(fld, .NumFields + 1)
                        Next

                        Dim rownum As Integer = 0

                        'if showing all station locations, create normal shape file with all stations; will add layer of normal points colored by station type
                        'if showing thematic map of values, shape file will contain only stations with data; will draw symbols of varying sizes

                        Dim minx As Double = Double.MaxValue, maxx As Double = -minx

                        For Each r As DataRow In dtStations.Rows
                            Dim x As Single = -Math.Abs(TestNull(r.Item("Longitude"), 0))
                            Dim y As Single = TestNull(r.Item("Latitude"), 0)
                            If Math.Abs(x) > 1 And y > 1 And Math.Abs(x) < 180 And y < 90 Then
                                minx = Math.Min(minx, x)
                                maxx = Math.Max(maxx, x)
                                Dim pnt As New MapWinGIS.Point
                                pnt.x = x
                                pnt.y = y
                                Dim shp As New MapWinGIS.Shape
                                If shp.Create(MapWinGIS.ShpfileType.SHP_POINT) AndAlso shp.InsertPoint(pnt, 0) AndAlso .EditInsertShape(shp, .NumShapes) Then
                                    For colnum As Integer = 0 To dtStations.Columns.Count - 1
                                        If Not IsDBNull(r.Item(colnum)) Then
                                            If dtStations.Columns(colnum).DataType Is GetType(String) Then
                                                .EditCellValue(colnum, .NumShapes - 1, CStr(r.Item(colnum)))
                                            ElseIf dtStations.Columns(colnum).DataType Is GetType(Integer) Then
                                                .EditCellValue(colnum, .NumShapes - 1, CInt(r.Item(colnum)))
                                            Else
                                                .EditCellValue(colnum, .NumShapes - 1, CDbl(r.Item(colnum)))
                                            End If
                                        End If
                                    Next
                                Else
                                    WarningMsg("Unable to create shape")
                                End If
                            Else
                            End If
                            rownum += 1
                            WriteProgress(rownum, dtStations.Rows.Count - 1, "Creating WRDB stations shape file...")
                        Next

                        WriteProgress("Saving shape file...")

                        .StopEditingTable(True)
                        .StopEditingShapes(True, True)
                        NumShapes = .NumShapes
                        .Projection = LatLongProjection
                        .SaveAs(WRDBShapeFileName)
                        .Close()

                        If NumShapes = 0 Then WarningMsg("No stations have been added to the WRDB Stations layer because none of the selected data items referenced stations with valid latitude and longitude.")

                        'need to reproject to the current setting for the MW project 

                        If NumShapes > 0 And ProjectProjection <> "" And Not GISMap.IsSameProjection(LatLongProjection, ProjectProjection) Then
                            Reproject(GISMap, WRDBShapeFileName, ProjectProjection, True)
                        End If
                    Else
                        ErrorMsg(GISMap.get_ErrorMsg(GISMap.LastErrorCode))
                    End If

                    'shape file is created, now add to view

                    ActiveLayer = clsLayer.Add(GISMap, WRDBShapeFileName)
                    If ActiveLayer Is Nothing Then Exit Sub
                    With CType(ActiveLayer, clsLayerPoint)
                        .Name = WRDBLayerName
                        .PointSize = 8
                        .PointType = MapWinGIS.tkPointType.ptTriangleDown
                        WRDBLayerHandle = .LayerHandle
                    End With

                    'reopen so is available elsewhere
                    WRDBShapefile.Open(WRDBShapeFileName)

                End With
                dtStations.Dispose()

            Catch ex As Exception
                ErrorMsg(, ex)
            Finally
                'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
                ProgressComplete()
            End Try
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub frmGIS_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
        If txtLocation.Focused And e.KeyChar = Chr(13) Then
            LocatePlace()
            e.Handled = True
        ElseIf e.KeyChar = Chr(27) Then
            IsCancelled = True
        End If
    End Sub

#Region "GISMap Event Handling"

    Private startext As MapWinGIS.Extents
    Private startx, starty As Double
    Private isPanning As Boolean = False

    Private Sub GISMap_ExtentsChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GISMap.ExtentsChanged
        If isPanning Then Exit Sub
        'always reset after a pan or zoom to select mode
        GISMap.CursorMode = MapWinGIS.tkCursorMode.cmSelection
        lstCoords.Clear() 'force recomputation of coordinates in FindPoints
        'IsModified = True
    End Sub

    Private Sub GISMap_MouseDownEvent(ByVal sender As Object, ByVal e As AxMapWinGIS._DMapEvents_MouseDownEvent) Handles GISMap.MouseDownEvent
        If e.button = 2 Then
            Me.ContextMenuMap.Show(Me, Me.PointToClient(Control.MousePosition))
        ElseIf Mode = enumMode.AreaOfInterest And AOIDrawingHandle <> -1 Then
            GISMap.ClearDrawing(AOIDrawingHandle)
            AOIDrawingHandle = -1
        End If
    End Sub

    Private Sub GISMap_MouseWheelDownEvent(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseWheelDownEvent
        GISMap.PixelToProj(e.X, e.Y, startx, starty)
        startext = GISMap.Extents
        isPanning = True
    End Sub

    Private Sub GISMap_MouseWheelUpEvent(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseWheelUpEvent
        lstCoords.Clear() 'force recomputation of coordinates in FindPoints
        IsModified = True
        isPanning = False
        GISMap.Focus()
    End Sub

    Private Sub GISMap_MouseMoveEvent(ByVal sender As Object, ByVal e As AxMapWinGIS._DMapEvents_MouseMoveEvent) Handles GISMap.MouseMoveEvent
        Try
            Dim x, y As Double

            If e.button = 3 Then 'pressing wheel button
                GISMap.PixelToProj(e.x, e.y, x, y)
                Dim dx As Double = x - startx, dy As Double = y - starty
                Dim ext As MapWinGIS.Extents = GISMap.Extents
                With ext
                    .SetBounds(.xMin - dx, .yMin - dy, 0, .xMax - dx, .yMax - dy, 0)
                End With
                GISMap.Extents = ext
                Exit Sub
            End If

            Static lasttxt As String = ""

            'When mouse hovers over a valid shape, show the name of the GISTag on the status bar

            'If Not ActiveForm Is Me Or IsSelecting Or IsCancelled Then Exit Sub
            If IsSelecting Then Exit Sub

            If GISMap.CursorMode <> MapWinGIS.tkCursorMode.cmSelection Then Exit Sub

            GISMap.PixelToProj(e.x, e.y, x, y)

            Dim res As Boolean = MapWinGeoProc.SpatialReference.ProjectPoint(x, y, ProjectProjection, LatLongProjection)

            If Not ProgressBar.Visible Then Me.StatusLabel.Text = String.Format("{0:N5},{1:N5}", x, y)

            Dim txt As String = ""

            If WRDBShapefile IsNot Nothing AndAlso ActiveLayer IsNot Nothing AndAlso ActiveLayer.LayerHandle = WRDBLayerHandle Then
                For Each rec As Integer In FindShapes(e.x, e.y)
                    With WRDBShapefile
                        Select Case Mode
                            Case enumMode.Output
                                If dtResults Is Nothing Then Exit Sub
                                Dim sel() As DataRow = dtResults.Select(String.Format("Station_ID='{0}'", CStr(.CellValue(enumField.ID, rec)).Replace("'", "")))
                                For i As Integer = 0 To sel.Length - 1
                                    If (Mode <> enumMode.Animate And Mode <> enumMode.Output) Or sel(i).Item("MinDate") >= AnimationInfo.ThisDate Then
                                        txt &= String.Format("Station ID: {0}\n{1}\nNo. Data: {2}\nMin Value: {3:N3}\nMax Value: {4:N3}\nAvg Value: {5:N3}\nFirst Date: {6:d}\nLast Date: {7:d}\n------------------------------\n", _
                                        .CellValue(enumField.ID, rec), .CellValue(enumField.Name, rec), sel(i).Item("NumResult"), sel(i).Item("MinResult"), sel(i).Item("MaxResult"), sel(i).Item("AvgResult"), sel(i).Item("MinDate"), sel(i).Item("MaxDate")).Replace("\n", vbCrLf)
                                        Exit For
                                    End If
                                Next
                            Case enumMode.Display
                                txt &= String.Format("Station ID: {0}\n{1}\nType: {2}\nFreq: {3}\nElev: {4}\n------------------------------\n", _
                                     .CellValue(enumField.ID, rec), .CellValue(enumField.Name, rec), .CellValue(enumField.Type, rec), .CellValue(enumField.Freq, rec), .CellValue(enumField.Elev, rec)).Replace("\n", vbCrLf)
                            Case enumMode.Select, enumMode.Stations
                                txt &= String.Format("{0} - {1}", .CellValue(enumField.ID, rec), .CellValue(enumField.Name, rec))
                                ToolTip.AutomaticDelay = 200
                                If ToolTip.GetToolTip(GISMap) <> txt Then ToolTip.SetToolTip(GISMap, txt)
                                Exit Sub
                            Case enumMode.Animate, enumMode.AreaOfInterest
                                'don't show anything
                        End Select
                    End With
                Next
                If Mode = enumMode.Select Or Mode = enumMode.Stations Then ToolTip.Hide(GISMap)
            Else
                'display info from non-stations layer
                If ActiveLayer IsNot Nothing AndAlso ActiveLayer.FileName <> "" AndAlso dtPolygon IsNot Nothing Then
                    With ActiveLayer.ShapeFile
                        For i As Integer = 0 To .NumShapes - 1
                            If .PointInShape(i, x, y) Then
                                For j As Integer = 0 To .NumFields - 1
                                    txt &= String.Format("{0}: {1}" & vbCrLf, .Field(j).Name, .CellValue(j, i))
                                Next
                                If ActiveLayer.Name = AnimationInfo.DataLayer Then
                                    With AnimationInfo
                                        Dim fld As Integer = GetFieldIndex(ActiveLayer.ShapeFile, .DataField)
                                        Dim StaID As String = ActiveLayer.ShapeFile.CellValue(fld, i)
                                        Dim value As Double = GetCurrentValue(StaID)
                                        txt &= String.Format("\nCurrent Data Value:\nDate-time: {0:MM/dd/yyyy}\nDisplay: {1}\nAggregation: {2}\nPCode: {3}\nValue: {4:N3}", .ThisDate, cboDisplay.Text, cboAggregate.Text, cboPCode.Text.Split(" ")(0), value).Replace("\n", vbCrLf)
                                    End With
                                End If
                                Exit For
                            End If
                        Next
                    End With
                End If
            End If

            If txt <> lasttxt Then
                lblInfo.Text = txt
                lasttxt = txt
            End If
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub GISMap_MouseWheelScrollEvent(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseWheelScrollEvent
        Try
            Const PercentPerDelta = 15
            If e.Delta < 0 Then
                GISMap.ZoomOut(Math.Abs(e.Delta) / 100 * PercentPerDelta)
            Else
                GISMap.ZoomIn(Math.Abs(e.Delta) / 100 * PercentPerDelta)
            End If
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub GISMap_SelectBoxDrag(ByVal sender As Object, ByVal e As AxMapWinGIS._DMapEvents_SelectBoxDragEvent) Handles GISMap.SelectBoxDrag
        If GISMap.CursorMode = MapWinGIS.tkCursorMode.cmSelection And ModeSettings.AllowStationSelection Or Mode = enumMode.AreaOfInterest Then IsSelecting = True 'so mousemove events show tooltips
    End Sub

    Private Sub GISMap_SelectBoxFinal(ByVal sender As Object, ByVal e As AxMapWinGIS._DMapEvents_SelectBoxFinalEvent) Handles GISMap.SelectBoxFinal
        Try
            With GISMap
                If .CursorMode = MapWinGIS.tkCursorMode.cmSelection Then
                    Dim pxMin As Double, pxMax As Double, pyMin As Double, pyMax As Double
                    .PixelToProj(e.left, e.bottom, pxMin, pyMin)
                    .PixelToProj(e.right, e.top, pxMax, pyMax)

                    If ModeSettings.AllowStationSelection Then
                        IsSelecting = False
                        Dim myExtents As New MapWinGIS.Extents()
                        myExtents.SetBounds(pxMin, pyMin, 0, pxMax, pyMax, 0)

                        'Check if there are any shapes with in the shapefile that intersect with the selection box
                        Dim selectedShapes() As Integer = Nothing
                        If WRDBShapefile.SelectShapes(myExtents, 0, MapWinGIS.SelectMode.INTERSECTION, selectedShapes) Then
                            If Not (My.Computer.Keyboard.CtrlKeyDown Or My.Computer.Keyboard.ShiftKeyDown) Then
                                lstStations.Clear()
                                RefreshDisplay()
                            End If
                            For i As Integer = 0 To UBound(selectedShapes)
                                If .get_ShapeVisible(WRDBLayerHandle, selectedShapes(i)) Then
                                    Dim StationID As String = WRDBShapefile.CellValue(0, selectedShapes(i))
                                    lstStations.Add(StationID)
                                    Dim clr As System.Drawing.Color = .get_ShapePointColor(WRDBLayerHandle, selectedShapes(i))
                                    .set_ShapePointColor(WRDBLayerHandle, selectedShapes(i), MapColor(Color.Yellow))
                                End If
                            Next
                            lblInfo.Text = "Selected Stations:"
                            For Each s As String In lstStations
                                lblInfo.Text &= vbCrLf & s
                            Next
                            btnSelect.Visible = lstStations.Count > 0
                        End If
                    ElseIf Mode = enumMode.AreaOfInterest Then
                        If AOIDrawingHandle = -1 Then AOIDrawingHandle = .NewDrawing(MapWinGIS.tkDrawReferenceList.dlSpatiallyReferencedList)
                        GISMap.DrawLineEx(AOIDrawingHandle, pxMin, pyMin, pxMax, pyMin, 2, MapColor(Color.Red))
                        GISMap.DrawLineEx(AOIDrawingHandle, pxMax, pyMin, pxMax, pyMax, 2, MapColor(Color.Red))
                        GISMap.DrawLineEx(AOIDrawingHandle, pxMax, pyMax, pxMin, pyMax, 2, MapColor(Color.Red))
                        GISMap.DrawLineEx(AOIDrawingHandle, pxMin, pyMax, pxMin, pyMin, 2, MapColor(Color.Red))
                        'convert to lat/long
                        MapWinGeoProc.SpatialReference.ProjectPoint(pxMin, pyMin, ProjectProjection, LatLongProjection)
                        MapWinGeoProc.SpatialReference.ProjectPoint(pxMax, pyMax, ProjectProjection, LatLongProjection)
                        _AOIRectangle = New RectangleF(pxMin, pyMin, pxMax - pxMin, pyMax - pyMin)
                        btnSelect.Text = "Return Area of Interest"
                        btnSelect.Visible = True
                    End If
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

#End Region

    ''' <summary>
    ''' Load settings from specified .GIS file
    ''' </summary>
    Private Sub LoadSettings(ByRef _FileName As String)
        Try
            If Not My.Computer.FileSystem.FileExists(_FileName) Then Exit Sub

            IsCancelled = False

            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
            If AnimationInfo.dsData IsNot Nothing Then AnimationInfo.dsData.Dispose() : AnimationInfo.dsData = Nothing
            If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
            If dtResults IsNot Nothing Then dtResults.Clear() : dtResults.Dispose() : dtResults = Nothing

            GC.Collect()

            Dim IniFile As clsWRDBIniFile = Nothing
            IniFile = New clsWRDBIniFile(_FileName)

            With IniFile


                GISMap.RemoveAllLayers()
                Dim NumGIS As Integer = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, "0"))
                Dim StaColor As Drawing.Color = Color.Red, StaType As MapWinGIS.tkPointType = MapWinGIS.tkPointType.ptTriangleDown
                For i As Integer = NumGIS - 1 To 0 Step -1
                    Dim FileName As String = .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISShapeFile, "")
                    If FileName.Trim = "" Then Continue For
                    Dim FileNameRelative As String = .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISShapeFileRelative, "")
                    If IO.Path.GetFileNameWithoutExtension(FileName).Equals(WRDBLayerName, StringComparison.OrdinalIgnoreCase) Then
                        StaColor = Drawing.Color.FromName(.GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISPointColor, "Red"))
                        StaType = [Enum].Parse(GetType(MapWinGIS.tkPointType), .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISPointType, MapWinGIS.tkPointType.ptTriangleDown.ToString))
                    Else
                        FileName = FindFile(FileName, IO.Path.GetDirectoryName(.FileName), FileNameRelative)
                        If FileName <> "" Then
                            ActiveLayer = clsLayer.Add(GISMap, FileName)
                            If ActiveLayer Is Nothing Then Continue For
                            ActiveLayer.Load(IniFile, i)
                            RefreshLayerList()
                        End If
                    End If
                    If IsCancelled Then Exit Sub
                Next
                If IniFile IsNot Nothing Then .Save()

                If Mode <> enumMode.Animate And Mode <> enumMode.AreaOfInterest AndAlso My.Computer.FileSystem.FileExists(WRDBShapeFileName) Then 'add stations layer back

                    Dim NewLayer As clsLayerPoint = clsLayer.Add(GISMap, WRDBShapeFileName)
                    If NewLayer IsNot Nothing Then
                        With NewLayer
                            .Name = WRDBLayerName
                            .PointSize = 8
                            .PointColor = StaColor
                            .PointType = StaType
                            WRDBLayerHandle = .LayerHandle
                            WRDBShapefile = .ShapeFile
                        End With

                        'reopen so is available elsewhere
                        WRDBShapefile.Open(WRDBShapeFileName)
                    End If
                End If

                Dim xmin, xmax, ymin, ymax As Double
                xmin = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMin, "-1"))
                xmax = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMax, "-1"))
                ymin = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMin, "-1"))
                ymax = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMax, "-1"))
                Dim ext As MapWinGIS.Extents = GISMap.Extents
                If xmin <> -1 Then
                    ext.SetBounds(xmin, ymin, 0, xmax, ymax, 0)
                    GISMap.Extents = ext
                End If
                AnimationInfo.DataLayer = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataLayer, "")
                AnimationInfo.DataField = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataField, "")
                Dim DataFile As String = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFile, "")
                Dim DataFileRelative As String = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFileRelative, "")
                Dim lyrFound As Boolean = False
                For i As Integer = 0 To GISMap.NumLayers - 1
                    If GISMap.get_LayerName(GISMap.get_LayerPosition(i)).Equals(AnimationInfo.DataLayer, StringComparison.OrdinalIgnoreCase) Then lyrFound = True : Exit For
                Next
                If Not lyrFound Then AnimationInfo.DataLayer = ""
                If DataFile <> "" AndAlso AnimationInfo.DataLayer <> "" AndAlso AnimationInfo.DataField <> "" AndAlso FindFile(DataFile, IO.Path.GetDirectoryName(.FileName), DataFileRelative) <> "" Then
                    AssignData(DataFile, AnimationInfo.DataLayer, AnimationInfo.DataField)
                    If IsCancelled Then Exit Sub
                End If
                AnimationInfo.Colors(0) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor0, Color.LightGray.Name))
                AnimationInfo.Colors(1) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor1, Color.Green.Name))
                AnimationInfo.Colors(2) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor2, Color.Yellow.Name))
                AnimationInfo.Colors(3) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor3, Color.Red.Name))
                AnimationInfo.Colors(4) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor4, Color.White.Name))
                AnimationInfo.InvertColor = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataInvert, "False")
                AnimationInfo.ThresholdType = [Enum].Parse(GetType(enumThreshold), .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdType, enumThreshold.None.ToString))
                AnimationInfo.ThresholdValue = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdValue, "0")

                SuspendMapUpdate = True

                lstLayers.SelectedIndex = IIf(lstLayers.Items.Count = 0, -1, 0)
                cboDisplay.Text = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Display, "Average Values")
                cboPCode.Text = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_PCode, "")
                cboAggregate.SelectedIndex = [Enum].Parse(GetType(enumAggregate), .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Aggregate, enumAggregate.None.ToString))

                AnimationInfo.ThisDate = CDate(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DateTime, Date.MinValue.ToString))
                If AnimationInfo.ThisDate = Date.MinValue Then AnimationInfo.ThisDate = AnimationInfo.MinDate
                AnimationInfo.Speed = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_AnimSpeed, "3")

                SuspendMapUpdate = False

            End With

        Catch ex As Exception
            ErrorMsg("An error occurred while trying to read the .GIS file.", ex)
        Finally
            IsModified = False
            ProgressComplete()
        End Try
    End Sub

    Private Sub LocatePlace()
        Try
            If txtLocation.Text = "" Then Exit Sub
            With WRDBShapefile
                Dim found As Boolean = False
                For i As Integer = 0 To .NumShapes
                    If .CellValue(0, i) = txtLocation.Text.Trim.ToUpper Then
                        found = True
                        Dim x As Single = .Shape(i).Point(0).x
                        Dim y As Single = .Shape(i).Point(0).y
                        Dim MyExtents As MapWinGIS.Extents = GISMap.Extents
                        With MyExtents
                            Dim dx As Single = .xMax - .xMin
                            Dim dy As Single = .yMax - .yMin
                            .SetBounds(x - dx / 2, y - dy / 2, .zMin, x + dx / 2, y + dy / 2, .zMax)
                            GISMap.Extents = MyExtents
                        End With
                        Exit For
                    End If
                Next
                If Not found Then
                    WarningMsg("Station ID {0} was not found.", txtLocation.Text)
                End If
            End With
            txtLocation.Text = ""
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuClose.Click
        Close()
    End Sub

    Private Sub mnuDataAggregate_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataAggregate.DropDownItemClicked
        Try
            Aggregate = mnuDataAggregate.DropDownItems.IndexOf(e.ClickedItem)
            tbAnim.Visible = Aggregate <> enumAggregate.POR
            mnuViewAnimate.Enabled = Aggregate <> enumAggregate.POR
            mnuViewAnimateSpeed.Enabled = Aggregate <> enumAggregate.POR
            CheckDropDownItem(mnuDataAggregate, e.ClickedItem)
            cboAggregate.Text = e.ClickedItem.Text.Replace("&", "")
            If Mode = enumMode.Output Then
                SummarizeStationData()
            Else
                ResetAnimation()
            End If
            RefreshDisplay()
            ProgressComplete()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuDataDisplay_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataDisplay.DropDownItemClicked
        Me.Refresh()
        Application.DoEvents()
        cboDisplay.Text = e.ClickedItem.Text
        CheckDropDownItem(mnuDataDisplay, e.ClickedItem)
    End Sub

    Private Sub mnuDataPCode_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataPCode.DropDownItemClicked
        Application.DoEvents()
        cboPCode.Text = e.ClickedItem.Text
        CheckDropDownItem(mnuDataDisplay, e.ClickedItem)
    End Sub

    Private Sub mnuFileClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileClose.Click, tbClose.Click
        Try
            With AnimationInfo
                If .dsData IsNot Nothing AndAlso WarningMsg("Are you sure you want to close the data table and detach it from the shape file?") = Windows.Forms.DialogResult.OK Then
                    If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
                    .dsData.Dispose()
                    .dsData = Nothing
                    .DataLayer = ""
                    .DataField = ""
                    .DataHandle = -1
                    'reset data mapping properties on map layers (stored in key field)
                    For i As Integer = 0 To GISMap.NumLayers - 1
                        GISMap.set_LayerKey(GISMap.get_LayerHandle(i), "")
                    Next
                    If .DataHandle <> -1 Then GISMap.set_LayerKey(.DataHandle, .dsData.FileName & ";" & .DataField)
                    Me.pgLayers.SelectedObject = ActiveLayer
                    picLegend.Visible = False
                    mnuViewLegend.Checked = False
                    GC.Collect()
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileOpen_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpen.Click, tbOpen.Click
        Try
            If GISMap.NumLayers = 0 Then
                WarningMsg("You cannot open a data file until you open a GIS polygon shape file to which the data will be mapped.")
                Exit Sub
            End If
            Dim FileName As String = ""
            With New OpenFileDialog
                .AddExtension = True
                .CheckFileExists = True
                .CheckPathExists = True
                .DefaultExt = ".bmd"
                .Filter = "All Data Formats|*.bmd;*.db;*.txt;*.csv;*.dbf;*.mdb;*.xls*;*.sdb;*.fdb;*.sdf;*.out;*.air|Binary Modeling Data Files (*.bmd)|*.bmd|Paradox Tables (*.db)|*.db|Tab-delimited Text Files (*.txt)|*.txt|Comma-separated Text Files (*.csv)|*.csv|dBase Tables (*.dbf)|*.dbf|Access Database Files (*.mdb)|*.mdb|Excel Spreadsheet Files (*.xls*)|*.xls*|Sqlite Database Files (*.sdb)|*.sdb|Firebird Database Files (*.fdb)|*.fdb|SQL Server CE Database Files (*.sdf)|*.sdf|LSPC Output Files (*.out)|*.out|LSPC Input Files (*.air)|*.air|EFDC Output Files (*.out)|*.out"
                .FilterIndex = GetSetting(REGAPP, Me.Name, "FileFilter", 0)
                .InitialDirectory = GetSetting(REGAPP, "General", "LastOpenDir", "")
                .Title = "Open Data File"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    SaveSetting(REGAPP, Me.Name, "FileFilter", .FilterIndex)
                    If Not My.Computer.FileSystem.FileExists(.FileName) Then .Dispose() : Exit Sub
                    FileName = .FileName
                Else
                    .Dispose()
                    Exit Sub
                End If
            End With
            SaveSetting(REGAPP, "General", "LastOpenDir", IO.Path.GetDirectoryName(FileName))

            With AnimationInfo
                If Not AssignData(FileName, "", "") Then
                    .DataLayer = ""
                    .DataField = ""
                    .DataHandle = -1
                    .dsData.Dispose()
                    .dsData = Nothing
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Function AssignData(ByVal DataFileName As String, ByVal LayerName As String, ByVal FieldName As String) As Boolean
        Try
            With AnimationInfo
                WriteProgress("Initializing data file...")
                .dsData = WRDB.DataSource.clsData.Instantiate(DataFileName)
                If .dsData Is Nothing Then Return Nothing
                'Select Case IO.Path.GetExtension(DataFileName.ToUpper)
                '    Case ".BMD"
                '        .dsData = New WRDB.DataSource.clsDataBMD
                '    Case ".DBF", ".DB"
                '        .dsData = New WRDB.DataSource.clsDataOleDB
                '    Case ".TXT", ".CSV"
                '        .dsData = New WRDB.DataSource.clsDataText
                '    Case ".OUT"
                '        Dim sr As New IO.StreamReader(DataFileName)
                '        Dim s As String = sr.ReadLine
                '        sr.Close()
                '        If s.StartsWith("TT") Then
                '            .dsData = New WRDB.DataSource.clsDataLspcOut
                '        Else
                '            .dsData = New WRDB.DataSource.clsDataEFDC
                '        End If
                '    Case ".AIR"
                '        .dsData = New WRDB.DataSource.clsDataLspcAir
                '    Case Else
                '        WarningMsg("Insupported data file type.")
                '        Return False
                '        'With New frmSelectTable
                '        '    .InitialDatabaseFile = FileName
                '        '    If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then tbl = .SelectedTable Else tbl = Nothing
                '        '    .Dispose()
                '        '    If tbl Is Nothing Then Exit Sub
                '        'End With
                'End Select

                '.dsData.Initialize(DataFileName)

                ProgressComplete()

                If Not (.dsData.ContainsFieldName("Station_ID") And .dsData.ContainsFieldName("Date_Time")) Then
                    WarningMsg("The selected data file does not contain the required fields: Station_ID and Date_Time.")
                    Return False
                End If

                Dim CheckShapes As Boolean = False

                If LayerName = "" Or FieldName = "" Then
                    With New frmSelectLayer(GISMap)
                        If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                            LayerName = .cboLayer.Text
                            FieldName = .cboField.Text
                            CheckShapes = True
                            .Dispose()
                        Else
                            .Dispose()
                            Return False
                        End If
                    End With
                End If

                .DataLayer = LayerName
                .DataField = FieldName
                .DataHandle = GetLayerHandle(GISMap, .DataLayer)
                cboPCode.Items.Clear()
                cboPCode.Items.AddRange(AnimationInfo.dsData.PCodes.ToArray)
                mnuDataPCode.DropDownItems.Clear()
                For Each s As String In AnimationInfo.dsData.PCodes
                    mnuDataPCode.DropDownItems.Add(s)
                    If mnuDataPCode.DropDownItems.Count > 50 Then Exit For
                Next
                'see what timestep is appropriate
                If .dsData.Read Then
                    Dim dt1 As Date = .dsData.Items("Date_Time")
                    If .dsData.Read Then
                        Dim dt2 As Date = .dsData.Items("Date_Time")
                        If dt2.Subtract(dt1).TotalHours <= 1 Then
                            .TimeUnits = enumTimeUnits.Minute
                            .TimeStep = dt2.Subtract(dt1).TotalMinutes
                        ElseIf dt2.Subtract(dt1).TotalDays <= 1 Then
                            .TimeUnits = enumTimeUnits.Hour
                            .TimeStep = dt2.Subtract(dt1).TotalHours
                        Else
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = dt2.Subtract(dt1).TotalDays
                        End If
                    End If
                    .dsData.Rewind()
                End If

                'this is list of data stations that have associated shapes on data layer
                .StationList = New Generic.List(Of String)
                Dim sf As MapWinGIS.Shapefile = GetShapeFile(GISMap, .DataLayer)
                Dim fld As Integer = GetFieldIndex(sf, .DataField)
                For i As Integer = 0 To sf.NumShapes - 1
                    Dim StaID As String = sf.CellValue(fld, i)
                    If Not .StationList.Contains(StaID) Then .StationList.Add(StaID)
                Next

                If CheckShapes Then
                    'get list of unique station IDs and see how many matches there are to shape file (get date range)
                    .MinDate = .dsData.MinDate
                    .MaxDate = .dsData.MaxDate
                    Dim lstStaData As Generic.List(Of String) = .dsData.StationIDs

                    'find number of missing shapes
                    Dim NumMissingShapes As Integer = 0
                    For Each StaID As String In lstStaData
                        If Not .StationList.Contains(StaID) Then NumMissingShapes += 1
                    Next

                    'find number of missing data stations
                    Dim NumMissingStations As Integer = 0
                    For Each StaID As String In .StationList
                        If Not lstStaData.Contains(StaID) Then NumMissingStations += 1
                    Next

                    If NumMissingShapes + NumMissingStations > 0 Then WarningMsg("There are some mismatches between the data layer shapefile and available Station IDs in the data file: \n\n\t\b {0} out of the total of {1} unique Station IDs in the data file are missing from the shape file\n\n\t\b {2} out of the total of {3} shapes in the data layer had no associated data stations\n\nAs a result, some shapes in the data layer will not be visible during animation.", NumMissingShapes, lstStaData.Count, NumMissingStations, .StationList.Count)
                    If cboPCode.Items.Count > 0 Then cboPCode.SelectedIndex = 0
                End If

                lstLayers.Text = .DataLayer
                'reset data mapping properties on map layers (stored in key field)
                For i As Integer = 0 To GISMap.NumLayers - 1
                    GISMap.set_LayerKey(GISMap.get_LayerHandle(i), "")
                Next
                If .DataHandle <> -1 Then GISMap.set_LayerKey(.DataHandle, .dsData.FileName & ";" & .DataField)
                ActiveLayer = clsLayer.Reset(GISMap, .DataLayer)
                Me.pgLayers.SelectedObject = ActiveLayer
            End With
            picLegend.Visible = True
            mnuViewLegend.Checked = True
            Return True
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        Finally
            ProgressComplete()
        End Try
    End Function

    Private Sub mnuFileOpenGroup_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpenGroup.Click, tbOpenGroup.Click
        Try
            'load last layers used
            If IsModified Then
                Select Case SaveChangesWarning(GISFileName)
                    Case Windows.Forms.DialogResult.Yes : If Not SaveSettings(GISFileName) Then Exit Sub
                    Case Windows.Forms.DialogResult.Cancel : Exit Sub
                End Select
            End If
            With New OpenFileDialog
                .AddExtension = True
                .CheckFileExists = True
                .CheckPathExists = True
                .DefaultExt = ".gis"
                .Filter = "WRDB GIS Files (*.gis)|*.gis"
                .FilterIndex = 0
                .Title = "Load GIS File"
                .ValidateNames = True
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    GISFileName = .FileName
                    ResetTitleBar()
                    .Dispose()
                Else
                    Exit Sub
                    .Dispose()
                End If
            End With
            LoadSettings(GISFileName)
            If Mode = enumMode.Animate Then SummarizePolygonData()
            ResetAnimation()
            ResetTrackbar()
            RefreshDisplay()

            ' Call sub to add this file as an MRU
            mru.AddFile(GISFileName)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileOpenLast_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpenLast.Click, tbOpenLast.Click
        Try
            If mnuFileRecent.DropDownItems.Count > 0 Then mnuFileRecent.DropDownItems(0).PerformClick()
            'LoadSettings(ModeSettings.LastSettingsFilename)
            'If Mode = enumMode.Animate Then SummarizePolygonData()
            'ResetAnimation()
            'ResetTrackbar()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileSaveGroup_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileSaveGroup.Click, tbSave.Click
        SaveSettings(GISFileName)
    End Sub

    Private Sub mnuFileSaveGroupAs_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileSaveGroupAs.Click, tbSaveAs.Click
        If SaveSettings(GISFileName, True) Then mru.AddFile(GISFileName)
    End Sub

    Private Sub mnuHelp_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuHelpAbout.Click, mnuHelpContents.Click, mnuHelpEMail.Click, mnuHelpWeb.Click, mnuHelpWhatsThis.Click
        Try
            Select Case mnuHelp.DropDownItems.IndexOf(sender)
                Case 0 : ShowHelp()
                Case 1 : VB6.WhatsThisMode(Me)
                Case 3 : Process.Start("http://www.wrdb.com")
                Case 4 : Process.Start("mailto:cwilson@wileng.com")
                Case 6
                    With New frmAbout
                        .ShowDialog(Me)
                        .Dispose()
                    End With
                    'VB6.ShowForm(frmAbout, VB6.FormShowConstants.Modal, Me)
                Case Else
                    WarningMsg("This is not yet implemented.")
            End Select
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerAdd.Click, mnuLayerAdd2.Click, tbLayerAdd.Click
        Try
            AddLayer()
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerDown_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerDown.Click, mnuLayerDown2.Click, tbLayerDown.Click
        Try
            ActiveLayer.MoveDown()
            RefreshLayerList()
            'If lstLayers.SelectedIndex <> lstLayers.Items.Count - 1 Then lstLayers.SelectedIndex += 1
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerRemove_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerRemove.Click, mnuLayerRemove2.Click, tbLayerRemove.Click
        Try
            ActiveLayer.Remove()
            RefreshLayerList()
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerRemoveAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerRemoveAll.Click, mnuLayerRemoveAll2.Click, tbRemoveAll.Click
        Try
            GISMap.RemoveAllLayers()
            RefreshLayerList()
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerTop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerTop.Click, mnuLayerTop2.Click, tbLayerTop.Click
        Try
            ActiveLayer.MoveTop()
            RefreshLayerList()
            lstLayers.SelectedIndex = 0
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerUp_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerUp.Click, mnuLayerUp2.Click, tbLayerUp.Click
        Try
            ActiveLayer.MoveUp()
            RefreshLayerList()
            'If lstLayers.SelectedIndex <> 0 Then lstLayers.SelectedIndex -= 1
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLocate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLocate.Click
        Try
            txtLocation.Text = InputBox("Enter the Station ID you wish to use as a new center point on the map:", "Locate", "")
            btnLocation.PerformClick()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuView_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuZoomWindow.Click, mnuZoomIn.Click, mnuZoomOut.Click, mnuZoomPrevious.Click, mnuZoomExtents.Click, mnuPan.Click, mnuCopyToClipboard.Click, mnuZoomWindow2.Click, mnuZoomIn2.Click, mnuZoomOut2.Click, mnuZoomPrevious2.Click, mnuZoomExtents2.Click, mnuPan2.Click, mnuCopyClipboard2.Click, tbZoomWindow.Click, tbZoomIn.Click, tbZoomOut.Click, tbZoomPrevious.Click, tbZoomExtents.Click, tbPan.Click, tbCopy.Click
        Try
            With GISMap
                If sender Is mnuZoomWindow Or sender Is mnuZoomWindow2 Or sender Is tbZoomWindow Then
                    .CursorMode = MapWinGIS.tkCursorMode.cmZoomIn
                ElseIf sender Is mnuZoomIn Or sender Is mnuZoomIn2 Or sender Is tbZoomIn Then
                    .ZoomIn(.ZoomPercent)
                ElseIf sender Is mnuZoomOut Or sender Is mnuZoomOut2 Or sender Is tbZoomOut Then
                    .ZoomOut(.ZoomPercent)
                ElseIf sender Is mnuZoomPrevious Or sender Is mnuZoomPrevious2 Or sender Is tbZoomPrevious Then
                    .ZoomToPrev()
                ElseIf sender Is mnuZoomExtents Or sender Is mnuZoomExtents2 Or sender Is tbZoomExtents Then
                    '.ZoomToMaxExtents()
                    ZoomExtents()
                ElseIf sender Is mnuPan Or sender Is mnuPan2 Or sender Is tbPan Then
                    .CursorMode = MapWinGIS.tkCursorMode.cmPan
                ElseIf sender Is mnuCopyToClipboard Or sender Is mnuCopyClipboard2 Or sender Is tbCopy Then
                    Application.DoEvents() 'to make context menu disappear
                    CopyMap(GISMap)
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewAnimate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewAnimate.Click
        Try
            tbAnim.Visible = True
            tbAnimPlay.Checked = mnuViewAnimate.Checked
            AnimTimer.Enabled = tbAnimPlay.Checked
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewAttributes_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewAttributes.Click, tbAttrTable.Click
        Try
            'If AnimationInfo.DataLayer = "" Then
            '    WarningMsg("You must open a data table and attach a polygon shapefile before you can view its attributes.")
            '    Exit Sub
            'End If
            With New frmTable
                '.SetShapeFile(GISMap.get_GetObject(AnimationInfo.DataHandle))
                .SetShapeFile(GISMap.get_GetObject(ActiveLayer.LayerHandle))
                .ShowDialog(Me)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewData_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewData.Click, tbDataTable.Click
        Try
            If AnimationInfo.dsData Is Nothing Then
                WarningMsg("You must open a data table before you can view it.")
                Exit Sub
            End If
            With New frmTable
                .SetDataSource(AnimationInfo.dsData)
                .ShowDialog(Me)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    'Private Sub trkAnim_VisibleChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trkAnim.VisibleChanged
    '    If trkAnim.Visible Then ResetAnimationLimits()
    'End Sub

    ''' <summary>
    ''' Clear statusbar text and progress
    ''' </summary>
    Public Sub ProgressComplete()
        Try
            ProgressBar.Visible = False
            If Not AnimTimer.Enabled Then StatusLabel.Text = ""
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = False
            Application.DoEvents()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Function DisplayMode() As enumDisplay
        Return cboDisplay.SelectedIndex + 1 'much faster
        'If cboDisplay.Text Like "*No.*" Then
        '    Return enumDisplay.NumResult
        'ElseIf cboDisplay.Text Like "*Minimum*" Then
        '    Return enumDisplay.MinResult
        'ElseIf cboDisplay.Text Like "*Maximum*" Then
        '    Return enumDisplay.MaxResult
        'ElseIf cboDisplay.Text Like "*Average*" Then
        '    Return enumDisplay.AvgResult
        'End If
        Return enumDisplay.None
    End Function

    Private Sub pgLayers_PropertyValueChanged(ByVal s As Object, ByVal e As System.Windows.Forms.PropertyValueChangedEventArgs) Handles pgLayers.PropertyValueChanged
        IsModified = True
    End Sub

    ''' <summary>
    ''' Reset display attributes (colors and/or sizes) of station points
    ''' </summary>
    Private Sub RefreshDisplay()
        Try
            If IsCancelled Or SuspendMapUpdate Then Exit Sub

            If WRDBShapefile IsNot Nothing Then
                If Mode <> enumMode.Animate Then
                    If Not GISMap.get_LayerVisible(WRDBLayerHandle) Or (cboPCode.Visible And cboPCode.Text = "") Then Exit Sub
                    ActiveLayer = clsLayer.Reset(GISMap, WRDBLayerName)
                    GISMap.set_ShapeLayerPointColor(WRDBLayerHandle, MapColor(GISMap.get_ShapeLayerPointColor(WRDBLayerHandle)))
                    GISMap.set_LayerVisible(WRDBLayerHandle, False)
                End If

                With GISMap
                    .ClearLabels(WRDBLayerHandle)
                    .LayerFont(WRDBLayerHandle, "Arial", 8)
                    .set_LayerLabelsScale(WRDBLayerHandle, False)
                    .set_LayerLabelsShadow(WRDBLayerHandle, False)
                    .set_LayerLabelsOffset(WRDBLayerHandle, -4)
                    .set_UseLabelCollision(WRDBLayerHandle, True)
                End With
            End If

            If AnimationInfo.dsData IsNot Nothing Then
                With GISMap
                    .ClearLabels(AnimationInfo.DataHandle)
                    .LayerFont(AnimationInfo.DataHandle, "Arial", 8)
                    .set_LayerLabelsScale(AnimationInfo.DataHandle, False)
                    .set_LayerLabelsShadow(AnimationInfo.DataHandle, False)
                    .set_LayerLabelsOffset(AnimationInfo.DataHandle, -4)
                    .set_UseLabelCollision(AnimationInfo.DataHandle, True)
                End With
            End If

            Select Case Mode
                Case enumMode.Output

                    Const minsize As Integer = 4, maxsize As Integer = 16

                    With GISMap

                        .ClearLabels(WRDBLayerHandle)
                        .LayerFont(WRDBLayerHandle, "Arial", 8)
                        .set_LayerLabelsScale(WRDBLayerHandle, False)
                        .set_LayerLabelsShadow(WRDBLayerHandle, False)

                        If dtResults IsNot Nothing Then

                            dtResults.DefaultView.RowFilter = String.Format("MinDate>=#{0}# AND MinDate<#{1}#", AnimationInfo.ThisDate, AnimationInfo.NextDate)
                            dtResults.DefaultView.Sort = "Station_ID"

                            'set shape size
                            For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                                Dim row As Integer = dtResults.DefaultView.Find(WRDBShapefile.CellValue(0, r))
                                Dim value As Single = 0
                                If row <> -1 Then value = TestNull(dtResults.DefaultView.Item(row).Item(DisplayMode.ToString), 0)
                                If value <> 0 Then
                                    Dim size As Single
                                    If DisplayMode() = enumDisplay.NumResult Then
                                        size = (value - AnimationInfo.MinNum) / (AnimationInfo.MaxNum - AnimationInfo.MinNum + 0.0001) * (maxsize - minsize) + minsize
                                    Else
                                        size = (value - AnimationInfo.MinValue) / (AnimationInfo.MaxValue - AnimationInfo.MinValue + 0.0001) * (maxsize - minsize) + minsize
                                    End If
                                    .set_ShapePointSize(WRDBLayerHandle, r, size)
                                    .set_ShapeVisible(WRDBLayerHandle, r, True)

                                Else
                                    .set_ShapeVisible(WRDBLayerHandle, r, False)
                                End If
                                If Not AnimTimer.Enabled Then
                                    If mnuViewLabels.Checked And value <> 0 Then
                                        Dim x, y As Single
                                        With WRDBShapefile.QuickExtents(r)
                                            x = (.xMin + .xMax) / 2
                                            y = (.yMin + .yMax) / 2
                                        End With
                                        .AddLabel(WRDBLayerHandle, value.ToString(IIf(DisplayMode() = enumDisplay.NumResult, "0", "N3")), ColorToUInt(Color.Black), x, y, MapWinGIS.tkHJustification.hjCenter)
                                    End If
                                    WriteProgress(r, WRDBShapefile.NumShapes - 1, "Updating display...")
                                End If
                            Next
                            ProgressComplete()
                            StatusLabel.Text = AnimationInfo.ThisDate.ToString(MyDateTimeFormat)
                        End If
                    End With

                Case enumMode.Display, enumMode.Select, enumMode.Stations

                    'set color based on unique value of station type and display for all stations
                    GISMap.set_ShapeLayerPointSize(WRDBLayerHandle, GISMap.get_ShapeLayerPointSize(WRDBLayerHandle))
                    GISMap.set_ShapeLayerPointColor(WRDBLayerHandle, MapColor(Color.Red))
                    For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                        Dim value As String = TestNull(WRDBShapefile.CellValue(enumField.Type, r), "")
                        Dim ptcolor As Color = Color.Black
                        'If value = cboDisplay.Text OrElse (cboDisplay.SelectedIndex = 0 AndAlso cboDisplay.Items.Contains(value)) Then
                        If value = cboDisplay.Text OrElse cboDisplay.SelectedIndex = 0 Then
                            If value <> "" Then
                                Dim index As Integer = cboDisplay.Items.IndexOf(value)
                                If index <> -1 Then ptcolor = DefaultLayerColor(index - 1)
                            End If
                            GISMap.set_ShapePointColor(WRDBLayerHandle, r, MapColor(ptcolor))
                            GISMap.set_ShapeVisible(WRDBLayerHandle, r, True)
                            If mnuViewLabels.Checked Then
                                Dim x, y As Single
                                With WRDBShapefile.QuickExtents(r)
                                    x = (.xMin + .xMax) / 2
                                    y = (.yMin + .yMax) / 2
                                End With
                                Dim StaID As String = TestNull(WRDBShapefile.CellValue(enumField.ID, r), "")
                                GISMap.AddLabel(WRDBLayerHandle, StaID, ColorToUInt(Color.Black), x, y, MapWinGIS.tkHJustification.hjCenter)
                            End If
                        Else
                            GISMap.set_ShapeVisible(WRDBLayerHandle, r, False)
                        End If
                        WriteProgress(r, WRDBShapefile.NumShapes - 1, "Updating display...")
                    Next

                Case enumMode.Animate

                    'refill dtPolygon with data within time range

                    With AnimationInfo
                        If .ThisDate <> Date.MinValue Then dtPolygon = .dsData.GetDataTable(.dsData.StationIDs.ToArray, cboPCode.Text, .ThisDate, .NextDate.AddSeconds(-1))
                    End With

                    If dtPolygon Is Nothing Then Exit Sub
                    Dim sf As MapWinGIS.Shapefile = Nothing
                    Dim fld As Integer = -1
                    Dim hdl As Integer = -1

                    If Not String.IsNullOrEmpty(AnimationInfo.DataLayer) Then
                        sf = GetShapeFile(GISMap, AnimationInfo.DataLayer)
                        fld = GetFieldIndex(sf, AnimationInfo.DataField)
                        hdl = GetLayerHandle(GISMap, AnimationInfo.DataLayer)

                        'data table has all observations for all stations for selected pcode; set filter, find first record, and aggregate data
                        For r As Integer = 0 To sf.NumShapes - 1
                            Dim StaID As String = sf.CellValue(fld, r)
                            Dim value As Double = GetCurrentValue(StaID)
                            If value >= 0 Then
                                GISMap.set_ShapeFillColor(hdl, r, ColorToUInt(GradientColor((value - AnimationInfo.MinValue) / (AnimationInfo.MaxValue - AnimationInfo.MinValue + 0.0001))))
                                GISMap.set_ShapeVisible(hdl, r, True)
                            Else
                                GISMap.set_ShapeVisible(hdl, r, False)
                            End If
                            If IsCancelled Then Exit For
                            If Not AnimTimer.Enabled Then
                                If mnuViewLabels.Checked And value >= 0 Then
                                    Dim x, y As Single
                                    With sf.QuickExtents(r)
                                        x = (.xMin + .xMax) / 2
                                        y = (.yMin + .yMax) / 2
                                    End With
                                    GISMap.AddLabel(AnimationInfo.DataHandle, value.ToString("N3"), ColorToUInt(Color.Black), x, y, MapWinGIS.tkHJustification.hjCenter)
                                End If
                                'WriteProgress(r, sf.NumShapes - 1, "Updating display...")
                            End If
                        Next
                        ProgressComplete()
                        StatusLabel.Text = AnimationInfo.ThisDate.ToString(MyDateTimeFormat)
                    End If
            End Select
            picLegend.Refresh()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If Not tbAnim.Visible Then ProgressComplete()
            If Mode <> enumMode.Animate Then GISMap.set_LayerVisible(WRDBLayerHandle, True)
        End Try
    End Sub

    ''' <summary>
    ''' Get current aggregate value for specified station based on current animationinfo setting controlled by trackbar; return -1 if not found
    ''' </summary>
    Private Function GetCurrentValue(ByVal StationID As String) As Double
        With AnimationInfo
            Return GetCurrentValue(StationID, .ThisDate, .NextDate)
        End With
    End Function

    ''' <summary>
    ''' Get current aggregate value for specified station; return -1 if not found
    ''' </summary>
    Private Function GetCurrentValue(ByVal StationID As String, ByVal MinDate As Date, ByVal MaxDate As Date) As Double
        Try
            'Dim rowfilter As String = String.Format("Station_ID='{0}' AND Date_Time>=#{1}# AND Date_Time<#{2}#", StationID, MinDate, MaxDate)
            Dim rowfilter As String = String.Format("Station_ID='{0}'", StationID)

            Dim value As Double = -1

            Select Case DisplayMode()
                Case enumDisplay.NumResult : value = TestNull(dtPolygon.Compute("Count(Result)", rowfilter), -1)
                Case enumDisplay.MinResult : value = TestNull(dtPolygon.Compute("Min(Result)", rowfilter), -1)
                Case enumDisplay.MaxResult : value = TestNull(dtPolygon.Compute("Max(Result)", rowfilter), -1)
                Case enumDisplay.AvgResult : value = TestNull(dtPolygon.Compute("Avg(Result)", rowfilter), -1)
            End Select
            Return value
        Catch ex As Exception
            ErrorMsg(, ex)
            Return -1
            IsCancelled = True
        End Try
    End Function

    Friend Sub RefreshLayerList()
        Try
            With lstLayers
                Dim text As String = .Text
                .Items.Clear()
                For i As Integer = 0 To GISMap.NumLayers - 1
                    Dim hdl As Integer = GISMap.get_LayerHandle(GISMap.NumLayers - 1 - i)
                    .Items.Add(GISMap.get_LayerName(hdl))
                    .SetItemChecked(.Items.Count - 1, GISMap.get_LayerLabelsVisible(hdl))
                Next
                .Text = text
                If .SelectedIndex = -1 And .Items.Count > 0 Then .SelectedIndex = 0
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Set title bar to include name of just opened or saved group file
    ''' </summary>
    Private Sub ResetTitleBar()
        If GISFileName = "" Then
            Text = "WRDB GIS 5.0"
        Else
            Text = IO.Path.GetFileName(GISFileName) & " - WRDB GIS 5.0"
        End If
    End Sub

    Private Sub SetAnimationTimeStep()
        Try
            If Aggregate = enumAggregate.POR Then Exit Sub
            With AnimationInfo
                If .MinDate.Year = 9999 Then Exit Sub
                Select Case .TimeUnits
                    Case enumTimeUnits.Minute
                        .ThisDate = .MinDate.AddMinutes(trkAnim.Value)
                        .NextDate = .ThisDate.AddMinutes(.TimeStep)
                    Case enumTimeUnits.Hour
                        .ThisDate = .MinDate.AddHours(trkAnim.Value)
                        .NextDate = .ThisDate.AddHours(.TimeStep)
                    Case enumTimeUnits.Day
                        .ThisDate = .MinDate.AddDays(trkAnim.Value)
                        .NextDate = .ThisDate.AddDays(.TimeStep)
                    Case enumTimeUnits.Month
                        .ThisDate = New Date(.MinDate.Year + trkAnim.Value \ 12, trkAnim.Value Mod 12 + 1, 1)
                        .NextDate = .ThisDate.AddDays(DateTime.DaysInMonth(.ThisDate.Year, .ThisDate.Month))
                    Case enumTimeUnits.Year
                        .ThisDate = New Date(.MinDate.Year + trkAnim.Value, 1, 1)
                        .NextDate = .ThisDate.AddDays(IIf(DateTime.IsLeapYear(.ThisDate.Year), 366, 365))
                End Select
                trkAnim.Minimum = 0
                ToolTip.SetToolTip(trkAnim, .ThisDate.ToString(MyDateTimeFormat))
                StatusLabel.Text = .ThisDate.ToString(MyDateTimeFormat)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Only call this when displaying data by pcode and have attached data file
    ''' </summary>
    ''' <remarks></remarks>
    Friend Sub SummarizePolygonData()
        Try
            If IsCancelled Or AnimationInfo.dsData Is Nothing Then Exit Sub
            If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
            GC.Collect()

            With AnimationInfo.dsData
                WriteProgress("Summarizing data...")

                RemoveHandler .Progress, AddressOf Progress
                AddHandler .Progress, AddressOf Progress

                .Rewind()

                'assume that data are in increasing date-times and have standard column names

                Dim iDat As Integer = -1, iPcd As Integer = -1
                For i As Integer = 0 To .FieldNames.Count - 1
                    If .FieldNames(i).Equals("DATE_TIME", StringComparison.OrdinalIgnoreCase) Then iDat = i
                    If .FieldNames(i).Equals(cboPCode.Text, StringComparison.OrdinalIgnoreCase) Then iPcd = i
                Next
                If iDat = -1 Or iPcd = -1 Then
                    WarningMsg("The data file is missing the column: Date_Time, " & cboPCode.Text)
                    Exit Sub
                End If

                'add all observations for selected pcode into table

                MenuMain.Enabled = False
                tbMain.Enabled = False

                'maybe just for debugging
                Dim lstKeys As New Generic.List(Of String)

                'dtPolygon = .GetDataTable(.StationIDs.ToArray, cboPCode.Text)

                'If dtPolygon.Rows.Count > 0 Then dtPolygon.PrimaryKey = New DataColumn() {dtPolygon.Columns("Station_ID"), dtPolygon.Columns("Date_Time")}

                ResetAnimation()

            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
            MenuMain.Enabled = True
            tbMain.Enabled = True
        End Try
    End Sub

    Private Sub ResetAnimation()
        Try
            If SuspendMapUpdate Or IsCancelled Then Exit Sub
            With AnimationInfo

                If .dsData Is Nothing Then Exit Sub

                Select Case Aggregate
                    Case enumAggregate.POR 'period of record; don't group by time but summarize for all data
                        .TimeUnits = enumTimeUnits.Day
                        .TimeStep = 1
                    Case enumAggregate.None
                        .TimeUnits = enumTimeUnits.Day
                        .TimeStep = 1
                        Dim dt As TimeSpan = New TimeSpan(1, 0, 0)
                        .dsData.Rewind()
                        If .dsData.Read Then
                            Dim t1 As Date = .dsData.Items(1)
                            If .dsData.Read Then
                                Dim t2 As Date = .dsData.Items(1)
                                dt = t2.Subtract(t1)
                            End If
                        End If
                        .dsData.Rewind()
                        If dt.TotalHours <= 1 Then
                            .TimeUnits = enumTimeUnits.Minute
                            .TimeStep = dt.TotalMinutes
                        ElseIf dt.TotalDays <= 1 Then
                            .TimeUnits = enumTimeUnits.Hour
                            .TimeStep = dt.TotalHours
                        Else
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = dt.TotalDays
                        End If
                    Case enumAggregate.Month
                        .TimeUnits = enumTimeUnits.Month
                        .TimeStep = 1
                    Case enumAggregate.Growing 'need to recompute because SQL can't determine if in growing season
                        .TimeUnits = enumTimeUnits.Year
                        .TimeStep = 1
                    Case enumAggregate.Annual
                        .TimeUnits = enumTimeUnits.Year
                        .TimeStep = 1
                End Select
            End With

            'find min and max value for selected pcode for all station ids (only if station ID is in shape file) so can determine limits for trackbar and color range

            Dim staList As String = ""
            For Each StaID As String In AnimationInfo.StationList
                staList &= IIf(staList = "", "", ",") & "'" & StaID & "'"
            Next

            Dim MinDate As Date = Date.MaxValue
            Dim MaxDate As Date = Date.MinValue
            Dim MinValue, MaxValue As Double

            'If dtPolygon IsNot Nothing Then
            '    With dtPolygon
            '        If .Rows.Count > 0 Then
            '            'this is better, so data not referenced by shape file is ignored, but it is really slow!
            '            'MinDate = .Compute("MIN(Date_Time)", String.Format("Station_ID IN ({0})", staList))
            '            'MaxDate = .Compute("MAX(Date_Time)", String.Format("Station_ID IN ({0})", staList))
            '            'MinValue = .Compute("MIN(Result)", String.Format("Station_ID IN ({0})", staList))
            '            'MaxValue = .Compute("MAX(Result)", String.Format("Station_ID IN ({0})", staList))
            '            'disabled following because dtPolygon is being filled at timestep only
            '            'MinDate = .Compute("MIN(Date_Time)", "")
            '            'MaxDate = .Compute("MAX(Date_Time)", "")
            '            'MinValue = .Compute("MIN(Result)", "")
            '            'MaxValue = .Compute("MAX(Result)", "")

            '        End If
            '    End With

            With AnimationInfo
                MinDate = .dsData.MinDate
                MaxDate = .dsData.MaxDate
                MinValue = .dsData.MinValue(cboPCode.Text)
                MaxValue = .dsData.MaxValue(cboPCode.Text)

                If Double.IsNaN(MinValue) Then MinValue = 0
                If Double.IsNaN(MaxValue) Then MaxValue = 1

                If Aggregate = enumAggregate.None Then
                    .MinDate = MinDate
                    .MaxDate = MaxDate
                Else
                    .MinDate = New Date(MinDate.Year, 1, 1)
                    .MaxDate = New Date(MaxDate.Year + 1, 1, 1)
                End If
                .MinValue = MinValue
                .MaxValue = MaxValue
                trkAnim.SmallChange = .TimeStep
                trkAnim.LargeChange = .TimeStep
                trkAnim.TickFrequency = .TimeStep
                trkAnim.Minimum = 0
                Select Case .TimeUnits
                    Case enumTimeUnits.Minute : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalMinutes
                    Case enumTimeUnits.Hour : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalHours
                    Case enumTimeUnits.Day : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalDays
                    Case enumTimeUnits.Month : trkAnim.Maximum = (.MaxDate.Year - .MinDate.Year) * 12 - 1
                    Case enumTimeUnits.Year : trkAnim.Maximum = .MaxDate.Year - .MinDate.Year - 1
                End Select
                'If .ThisDate = Date.MinValue Then trkAnim.Value = trkAnim.Minimum
                trkAnim.Value = trkAnim.Minimum
                ResetTrackbar()

                'determine range of values for all stations and aggregated timesteps so can properly assign colors
                MinValue = Double.MaxValue
                MaxValue = Double.MinValue

                For i As Integer = 0 To .dsData.StationIDs.Count - 1
                    Dim StaID As String = .dsData.StationIDs(i)
                    Dim dtMin, dtMax As Date
                    dtMin = .MinDate
                    Do
                        Dim rowfilter As String = String.Format("Station_ID='{0}'", StaID)
                        If Aggregate <> enumAggregate.POR And Aggregate <> enumAggregate.None Then
                            Select Case .TimeUnits
                                Case enumTimeUnits.Minute : dtMax = dtMin.AddMinutes(.TimeStep)
                                Case enumTimeUnits.Hour : dtMax = dtMin.AddHours(.TimeStep)
                                Case enumTimeUnits.Day : dtMax = dtMin.AddDays(.TimeStep)
                                Case enumTimeUnits.Month : dtMax = dtMin.AddDays(DateTime.DaysInMonth(dtMin.Year, dtMin.Month))
                                Case enumTimeUnits.Year : dtMax = dtMin.AddDays(IIf(DateTime.IsLeapYear(dtMin.Year), 366, 365))
                            End Select
                            'rowfilter &= String.Format(" AND Date_Time>=#{0}# AND Date_Time<#{1}#", dtMin, dtMax)
                        End If

                        Dim value As Double

                        If Aggregate = enumAggregate.None Then
                            Select Case DisplayMode()
                                Case enumDisplay.NumResult : value = 1
                                Case Else : value = TestNull(dtPolygon.Select(rowfilter)(0)(1), -1)
                            End Select
                        Else
                            Select Case DisplayMode()
                                Case enumDisplay.NumResult : value = TestNull(dtPolygon.Compute("Count(Result)", rowfilter), -1)
                                Case enumDisplay.MinResult : value = TestNull(dtPolygon.Compute("Min(Result)", rowfilter), -1)
                                Case enumDisplay.MaxResult : value = TestNull(dtPolygon.Compute("Max(Result)", rowfilter), -1)
                                Case enumDisplay.AvgResult : value = TestNull(dtPolygon.Compute("Avg(Result)", rowfilter), -1)
                            End Select
                        End If

                        If value <> -1 Then
                            MinValue = Math.Min(MinValue, value)
                            MaxValue = Math.Max(MaxValue, value)
                        End If

                        dtMin = dtMax
                    Loop Until dtMin > .MaxDate Or Aggregate = enumAggregate.POR Or Aggregate = enumAggregate.None
                    WriteProgress(i, .dsData.StationIDs.Count - 1, "Summarizing data...")
                Next
                If Aggregate = enumAggregate.POR Then
                    .ThisDate = .MinDate
                    .NextDate = .MaxDate
                End If
            End With
            'End If
            picLegend.Refresh()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    ''' <summary>
    ''' Only call this when displaying data by pcode
    ''' </summary>
    ''' <remarks></remarks>
    Friend Sub SummarizeStationData()
        Try
            With WRDBProject
                'If MainForm IsNot Nothing Then MainForm.WriteProgress("Summarizing data...", ProgressBarStyle.Marquee)
                WriteProgress("Summarizing data...")
                'query all stations in support table (or referenced in active working/master or temp table) 
                Select Case Mode
                    Case enumMode.Output
                        Dim sqlGroup As String = ""
                        Dim sqlWhere As String = ""
                        With AnimationInfo
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = 1
                        End With
                        trkAnim.Enabled = True
                        tbAnim.Enabled = True

                        Select Case Aggregate
                            Case enumAggregate.POR 'period of record; don't group by time but summarize for all data
                                trkAnim.Enabled = False
                                tbAnim.Enabled = False
                                With AnimationInfo
                                    .TimeUnits = enumTimeUnits.Year
                                    .ThisDate = Date.MinValue '.MinDate
                                    .NextDate = Date.MaxValue '.MaxDate
                                End With

                            Case enumAggregate.None
                                sqlGroup = ", Date_Time"
                            Case enumAggregate.Month
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", MONTH(Date_Time), YEAR(Date_Time)"
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%m',Date_Time), STRFTIME('%Y',Date_Time)"
                                    Case Else
                                        sqlGroup = ", EXTRACT(MONTH from Date_Time), EXTRACT(YEAR from Date_Time)"
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Month
                            Case enumAggregate.Growing
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", YEAR(Date_Time)"
                                        sqlWhere = String.Format(" AND MONTH(Date_Time) * 100 + DAY(Date_Time) >= {0} AND MONTH(Date_Time) * 100 + DAY(Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%Y',Date_Time)"
                                        sqlWhere = String.Format(" AND STRFTIME('%m',Date_Time) * 100 + STRFTIME('%D',Date_Time) >= {0} AND STRFTIME('%m',Date_Time) * 100 + STRFTIME('%D',Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                    Case Else
                                        sqlGroup = ", EXTRACT(YEAR from Date_Time)"
                                        sqlWhere = String.Format(" AND EXTRACT(MONTH FROM Date_Time) * 100 + EXTRACT(DAY FROM Date_Time) >= {0} AND EXTRACT(MONTH FROM Date_Time) * 100 + EXTRACT(DAY FROM Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Year
                            Case enumAggregate.Annual
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", YEAR(Date_Time)"
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%Y',Date_Time)"
                                    Case Else
                                        sqlGroup = ", EXTRACT(YEAR from Date_Time)"
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Year
                        End Select

                        Dim Sql As String = String.Format( _
                            "SELECT Station_ID, " & _
                            "COUNT(Result) as NumResult, " & _
                            "MIN(Result) as MinResult, " & _
                            "MAX(Result) as MaxResult, " & _
                            "AVG(Result) as AvgResult, " & _
                            "MIN(Date_Time) as MinDate, " & _
                            "MAX(Date_Time) as MaxDate " & _
                            "FROM {0} WHERE Station_ID IS NOT NULL AND PCode='{1}'{2} GROUP BY Station_ID{3}", TempData, cboPCode.Text, sqlWhere, sqlGroup)
                        If Not .DB.AddTable(dtResults, Sql) Then Exit Sub

                        dtResults.PrimaryKey = New DataColumn() {dtResults.Columns("Station_ID"), dtResults.Columns("MinDate")}

                        'find min and max value for selected pcode (only if station ID is in shape file)
                        Dim sb As New System.Text.StringBuilder
                        For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                            If sb.Length <> 0 Then sb.Append(",")
                            sb.Append(String.Format("'{0}'", WRDBShapefile.CellValue(0, r)))
                        Next
                        Dim staList As String = sb.ToString
                        'Dim staList As String = ""
                        'For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                        '    staList &= IIf(staList = "", "", ",") & "'" & WRDBShapefile.CellValue(0, r) & "'"
                        'Next

                        Dim MinNum, MaxNum As Integer
                        Dim MinValue, MaxValue As Double
                        Dim MinDate, MaxDate As Date

                        'With dtResults
                        '    Dim rowfilter As String = ""
                        '    If staList <> "" Then rowfilter = String.Format("Station_ID IN ({0})", staList)
                        '    MinDate = TestNull(.Compute("Min(MinDate)", rowfilter), Now) : Application.DoEvents()
                        '    MaxDate = TestNull(.Compute("Max(MinDate)", rowfilter), Now) : Application.DoEvents()
                        '    MinValue = TestNull(.Compute("Min(MinResult)", rowfilter), 0) : Application.DoEvents()
                        '    MaxValue = TestNull(.Compute("Max(MaxResult)", rowfilter), 0) : Application.DoEvents()
                        '    MinNum = TestNull(.Compute("Min(NumResult)", rowfilter), 0) : Application.DoEvents()
                        '    MaxNum = TestNull(.Compute("Max(NumResult)", rowfilter), 0) : Application.DoEvents()
                        'End With

                        'sqlite puts all but first column in datatable as objects, so .Compute fails

                        With dtResults.DefaultView
                            Dim rowfilter As String = ""
                            If staList <> "" Then rowfilter = String.Format("Station_ID IN ({0})", staList)
                            If rowfilter.Length < 5000 Then .RowFilter = rowfilter
                            If .Count > 0 Then
                                .Sort = "NumResult"
                                MinNum = .Item(0).Item("NumResult")
                                MaxNum = .Item(.Count - 1).Item("NumResult")
                                .Sort = "MinDate"
                                MinDate = .Item(0)("MinDate")
                                .Sort = "MaxDate"
                                MaxDate = .Item(.Count - 1)("MaxDate")
                                .Sort = "MinResult"
                                MinValue = .Item(0)("MinResult")
                                .Sort = "MaxResult"
                                MaxValue = .Item(.Count - 1)("MaxResult")
                            End If
                        End With

                        With AnimationInfo
                            .MinDate = New Date(MinDate.Year, 1, 1)
                            .MaxDate = New Date(MaxDate.Year + 1, 1, 1)
                            .MinValue = MinValue
                            .MaxValue = MaxValue
                            .MinNum = MinNum
                            .MaxNum = MaxNum

                            trkAnim.SmallChange = .TimeStep
                            trkAnim.LargeChange = .TimeStep
                            If .ThisDate = Date.MinValue Then trkAnim.Value = 0
                            trkAnim.Minimum = 0
                            Select Case .TimeUnits
                                Case enumTimeUnits.Minute
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalMinutes
                                Case enumTimeUnits.Hour
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalHours
                                Case enumTimeUnits.Day
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalDays
                                Case enumTimeUnits.Month
                                    trkAnim.Maximum = (.MaxDate.Year - .MinDate.Year) * 12 - 1
                                Case enumTimeUnits.Year
                                    trkAnim.Maximum = .MaxDate.Year - .MinDate.Year - 1
                            End Select
                            trkAnim.Value = 0
                            SetAnimationTimeStep()
                        End With
                End Select
                'If AnimationInfo.dsData IsNot Nothing Then SummarizePolygonData()
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
            ProgressComplete()
        End Try
    End Sub

    Private Sub tbAnim_ItemClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles tbAnim.ItemClicked
        Try
            With trkAnim
                If e.ClickedItem Is tbAnimBeg Then
                    tbAnimPause.PerformClick()
                    .Value = .Minimum
                ElseIf e.ClickedItem Is tbAnimEnd Then
                    tbAnimPause.PerformClick()
                    .Value = .Maximum
                ElseIf e.ClickedItem Is tbAnimNext Then
                    tbAnimPause.PerformClick()
                    If .Value + .SmallChange > .Maximum Then
                        .Value = .Minimum
                    Else
                        .Value += .SmallChange
                    End If
                ElseIf e.ClickedItem Is tbAnimPrev Then
                    tbAnimPause.PerformClick()
                    If .Value - .SmallChange < .Minimum Then
                        .Value = .Maximum
                    Else
                        .Value -= .SmallChange
                    End If
                ElseIf e.ClickedItem Is tbAnimPause Then
                    AnimTimer.Enabled = False
                    IsCancelled = True
                    tbAnimPlay.Checked = False
                    mnuViewAnimate.Checked = False
                ElseIf e.ClickedItem Is tbAnimPlay Then
                    AnimTimer.Enabled = Not tbAnimPlay.Checked
                    IsCancelled = Not AnimTimer.Enabled
                    mnuViewAnimate.Checked = Not tbAnimPlay.Checked
                ElseIf e.ClickedItem Is tbAnimClose Then
                    tbAnimPause.PerformClick()
                    tbAnim.Visible = False
                End If
            End With
            ToolTip.Active = Not AnimTimer.Enabled
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub tbAnim_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles tbAnim.Paint
        With trkAnim
            .Left = tbAnim.Left + tbAnimBeg.Width * 0.2
            .Top = tbAnim.Top + tbAnim.Height * 0.1
            .Height = tbAnim.Height * 0.8
            .Width = tbAnim.Width - tbAnimBeg.Width * 9
            .BringToFront()
        End With
    End Sub

    Private Sub tbAnim_VisibleChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tbAnim.VisibleChanged
        trkAnim.Visible = tbAnim.Visible
        'mnuToolbarAnim.Checked = tbAnim.Visible
        'mnuTbAnim.Checked = tbAnim.Visible
    End Sub

    Private Sub tbAnimSpeed_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles tbAnimSpeed.DropDownItemClicked, mnuViewAnimateSpeed.DropDownItemClicked
        Dim Index As Integer = sender.DropDownItems.IndexOf(e.ClickedItem)
        AnimationInfo.Speed = Index + 1
        AnimTimer.Interval = 1 + (4 - Index) * 100 'will range from 1 to 401 milliseconds delay
        CheckDropDownItem(mnuViewAnimateSpeed, Index)
        CheckDropDownItem(tbAnimSpeed, Index)
    End Sub

    Private Sub trkAnim_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles trkAnim.MouseDown
        AnimTimer.Enabled = False
    End Sub

    Private Sub trkAnim_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trkAnim.ValueChanged
        IsCancelled = False
        ResetTrackbar()
    End Sub

    Private Sub ResetTrackbar()
        Try
            If SuspendMapUpdate Or Not ModeSettings.AllowAnimation Then Exit Sub
            SetAnimationTimeStep()
            RefreshDisplay()
            ToolTip.Active = Not AnimTimer.Enabled
        Catch ex As Exception
            AnimTimer.Stop()
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Show text on statusbar (causes progressbar to be marquee)
    ''' </summary>
    ''' <param name="FormatString">Text to display on statuslabel (may include formatting text for String.Format)</param>
    ''' <param name="Args">Optional arguments to pass to FormatString</param>
    Public Sub WriteProgress(ByVal FormatString As String, ByVal ParamArray Args() As Object)
        Try
            ProgressBar.Visible = True
            ProgressBar.Style = ProgressBarStyle.Marquee
            StatusLabel.Text = String.Format(FormatString, Args)
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = True
            Application.DoEvents()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Show text and task progress on statusbar 
    ''' </summary>
    ''' <param name="Value">Current value for progressbar</param>
    ''' <param name="MaxValue">Maximum value for progressbar</param>
    ''' <param name="FormatString">Text to display on statuslabel (may include formatting text for String.Format)</param>
    ''' <param name="Args">Optional arguments to pass to FormatString</param>
    Public Sub WriteProgress(ByVal Value As Integer, ByVal MaxValue As Integer, ByVal FormatString As String, ByVal ParamArray Args() As Object)
        Try
            Static LastTimer As Integer = My.Computer.Clock.TickCount
            If My.Computer.Clock.TickCount - LastTimer < 500 Then Exit Sub 'don't let it update too frequently!
            With ProgressBar
                .Style = ProgressBarStyle.Blocks
                .Maximum = MaxValue
                .Value = Value
                .Visible = True
            End With
            StatusLabel.Text = String.Format(FormatString, Args)
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = True
            Application.DoEvents()
            LastTimer = My.Computer.Clock.TickCount
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub ZoomExtents()
        GISMap.ZoomToLayer(GetLayerHandle(GISMap, lstLayers.Text))
    End Sub

    Private Sub mnuFileExport_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileExport.Click, tbExport.Click
        Try
            Static AlreadyWarned As Boolean = False
            If Not AlreadyWarned Then WarningMsg("This will allow you to export data for the currently thematic map for the currently selected PCode, type of data, aggregation period, and time step to a separate export file which can be used in an external GIS application. The export file can be in either of two formats:\n\n\t\b Shape file: contains all polygon or point shapes and the attribute table will have the current data file values appended. The GIS application can use the data field to prepare a thematic map.\n\t\b dBase table: contains only the shape keys and data values. The GIS application can 'join' this table to the original shape file so that the data field can be used to display a thematic map.\n\nThis message will not be repeated this session.")
            AlreadyWarned = True
            Dim db As New clsDBDBase
            Dim ThisDateLabel As String = Choose(DisplayMode() + 1, "ALL", "NUM", "MIN", "MAX", "AVG") & "_" & cboPCode.Text.Split(" ")(0)
            If Aggregate = enumAggregate.POR Then
                ThisDateLabel &= "_POR"
            Else
                ThisDateLabel &= String.Format("_{0:yyyyMMdd}", AnimationInfo.ThisDate)
            End If
            Dim ExportFile As String = ""
            With New SaveFileDialog
                .AddExtension = True
                .CheckPathExists = True
                .CreatePrompt = False
                '.DefaultExt = ".DBF"
                .Filter = "Shape File (*.shp)|*.shp|dBase Table (*.dbf)|*.dbf"
                .FilterIndex = 0
                .OverwritePrompt = True
                .Title = "Export Data File"
                .ValidateNames = True
                .InitialDirectory = GetSetting(REGAPP, Me.Name, "AddLayer", My.Computer.FileSystem.SpecialDirectories.MyDocuments & "WRDB")
                .FileName = String.Format("WRDBGIS_{0}", ThisDateLabel)
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then ExportFile = .FileName
                .Dispose()
            End With

            If ExportFile = "" Then Exit Sub

            If IO.Path.GetExtension(ExportFile).ToUpper = ".SHP" Then
                'create new shape file with all shapes in source file plus data field
                Dim ShapeFileName As String = ""
                Select Case Mode
                    Case enumMode.Output
                        ShapeFileName = WRDBShapeFileName
                    Case enumMode.Animate
                        ShapeFileName = CType(GISMap.get_GetObject(AnimationInfo.DataHandle), MapWinGIS.Shapefile).Filename
                End Select

                MapWinGeoProc.DataManagement.DeleteShapefile(ExportFile)
                If Not MapWinGeoProc.DataManagement.CopyShapefile(ShapeFileName, ExportFile) Then Exit Sub

                Dim sf As New MapWinGIS.Shapefile
                If Not sf.Open(ExportFile) Then Exit Sub
                If Not sf.StartEditingTable() Then Exit Sub

                'see if data field already in table
                Dim FieldName As String = String.Format("{0}", ThisDateLabel)
                If FieldName.Length > 10 Then FieldName = FieldName.Substring(0, 10) '.dbf field name max len =10
                Dim fldidx As Integer = -1
                For i As Integer = 0 To sf.NumFields - 1
                    If sf.Field(i).Name = FieldName Then fldidx = i : Exit For
                Next

                If fldidx = -1 Then 'not found, add it
                    Dim fld As New MapWinGIS.Field
                    fld.Type = MapWinGIS.FieldType.DOUBLE_FIELD
                    fld.Name = FieldName
                    sf.EditInsertField(fld, sf.NumFields) : fldidx = sf.NumFields - 1
                End If

                Dim staidx As Integer = GetFieldIndex(sf, AnimationInfo.DataField)

                'update value for each shape
                For i As Integer = 0 To sf.NumShapes - 1
                    Dim StaID As String = sf.CellValue(staidx, i)
                    Dim Value As Double = GetCurrentValue(StaID, AnimationInfo.ThisDate, AnimationInfo.NextDate)
                    If Value >= 0 Then sf.EditCellValue(fldidx, i, Value)
                    WriteProgress(i, sf.NumShapes - 1, "Saving data...")
                Next

                'close file
                sf.StopEditingTable()
                sf.Close()

            Else
                'write stand-alone .dbf file that can be joined with original shape file
                'field names 10 char max, start with character, 128 fields max
                'get field names for each time step

                With db

                    .DatabaseType = clsDB.enumDBType.DBase
                    .DataSource = IO.Path.GetDirectoryName(ExportFile)
                    Dim TableName As String = IO.Path.GetFileNameWithoutExtension(ExportFile)
                    .CreateConnection()

                    Dim FieldNames As New Generic.List(Of String)
                    Dim FieldTypes As New Generic.List(Of clsDB.enumColType)

                    FieldNames.Add("Station_ID")
                    FieldTypes.Add(clsDB.enumColType.String)

                    FieldNames.Add(.GetValidObjectName(ThisDateLabel))
                    FieldTypes.Add(clsDB.enumColType.Double)

                    'if shapefile already exists, will be corrupted if dbf file has same name; delete shape file
                    MapWinGeoProc.DataManagement.DeleteShapefile(IO.Path.ChangeExtension(ExportFile, ".shp"))

                    If .TableExists(TableName) Then .DropTable(TableName)
                    If .CreateTable(TableName, FieldNames.ToArray, FieldTypes.ToArray) Then
                        Dim ds As New DataSet
                        Dim da As DbDataAdapter = .AddTable(ds, TableName)
                        If da IsNot Nothing Then
                            Dim dt As DataTable = ds.Tables(0)
                            Select Case Mode
                                Case enumMode.Output
                                    If dtResults IsNot Nothing Then
                                        dtResults.DefaultView.RowFilter = String.Format("MinDate>=#{0}# AND MinDate<#{1}#", AnimationInfo.ThisDate, AnimationInfo.NextDate)
                                        dtResults.DefaultView.Sort = "Station_ID"
                                        Dim FieldName As String = .GetValidObjectName(ThisDateLabel)
                                        For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                                            Dim row As Integer = dtResults.DefaultView.Find(WRDBShapefile.CellValue(0, r))
                                            Dim StaID As String = ""
                                            Dim value As Single = 0
                                            If row <> -1 Then
                                                StaID = TestNull(dtResults.DefaultView.Item(row).Item("Station_ID"), "")
                                                value = TestNull(dtResults.DefaultView.Item(row).Item(DisplayMode.ToString), 0)
                                            End If
                                            If StaID <> "" And value <> 0 Then
                                                Dim dr As DataRow = dt.NewRow
                                                dr.Item("Station_ID") = StaID
                                                dr.Item(FieldName) = value
                                                dt.Rows.Add(dr)
                                            End If
                                            WriteProgress(r, WRDBShapefile.NumShapes - 1, "Saving data...")
                                        Next
                                    End If
                                Case enumMode.Select, enumMode.Display
                                Case enumMode.Animate
                                    Dim sf As MapWinGIS.Shapefile = GISMap.get_GetObject(AnimationInfo.DataHandle)
                                    Dim fld As Integer = GetFieldIndex(sf, AnimationInfo.DataField)
                                    Dim FieldName As String = String.Format("{0}", .GetValidObjectName(ThisDateLabel))
                                    For r As Integer = 0 To sf.NumShapes - 1
                                        Dim StaID As String = sf.CellValue(fld, r)
                                        Dim dr As DataRow = dt.NewRow
                                        dr.Item("Station_ID") = StaID
                                        Dim Value As Double = GetCurrentValue(StaID, AnimationInfo.ThisDate, AnimationInfo.NextDate)
                                        If Value >= 0 Then
                                            dr.Item(FieldName) = Value
                                            dt.Rows.Add(dr)
                                        End If
                                        WriteProgress(r, sf.NumShapes - 1, "Saving data...")
                                    Next
                            End Select
                            WriteProgress("Saving data...")
                            Dim NumUpdated As Integer = da.Update(ds, TableName)
                            ds.Dispose()
                            da.Dispose()
                        End If
                    End If
                    .Dispose()
                End With
            End If

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    Private Sub picLegend_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles picLegend.Paint
        Try
            'create legend squares samewidth as picturebox, and height = 12 points

            With Me.picLegend
                .Font = New Font("Arial", 8)
                Dim g As Graphics = e.Graphics
                Dim BoxHeight As Int16 = g.MeasureString("0.00", .Font).Height * 1.5
                Dim NumEntries As Int16 = Int(.Height / BoxHeight)
                If NumEntries <= 1 Then Exit Sub
                For i As Int16 = 0 To NumEntries - 1
                    Dim br As New SolidBrush(GradientColor(i * 1.0 / (NumEntries - 1)))
                    g.FillRectangle(br, New Rectangle(0, BoxHeight * i, .Width, BoxHeight))
                    Dim fmt As New Drawing.StringFormat
                    fmt.Alignment = StringAlignment.Center
                    fmt.LineAlignment = StringAlignment.Center
                    Dim pnt As PointF
                    'draw white on black
                    pnt = New PointF(.Width / 2 + 1, (i + 0.5) * BoxHeight + 1)
                    Dim value As Double
                    With AnimationInfo
                        value = i / (NumEntries - 1) * (.MaxValue - .MinValue) + .MinValue
                    End With
                    g.DrawString(value.ToString("N3"), .Font, Brushes.Black, pnt, fmt)
                    pnt = New PointF(.Width / 2, (i + 0.5) * BoxHeight)
                    g.DrawString(value.ToString("N3"), .Font, Brushes.White, pnt, fmt)
                Next
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewLegend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewLegend.Click, tbLegend.Click
        mnuViewLegend.Checked = Not mnuViewLegend.Checked
        picLegend.Visible = mnuViewLegend.Checked
    End Sub

    Private Sub mnuDataOptions_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuDataOptions.Click
        Try
            With New frmGISOptions
                .cboColor0.Text = AnimationInfo.Colors(0).Name
                .cboColor1.Text = AnimationInfo.Colors(1).Name
                .cboColor2.Text = AnimationInfo.Colors(2).Name
                .cboColor3.Text = AnimationInfo.Colors(3).Name
                .cboColor4.Text = AnimationInfo.Colors(4).Name
                .chkInvert.Checked = AnimationInfo.InvertColor
                .cboThreshold.SelectedIndex = AnimationInfo.ThresholdType
                .txtThreshold.Text = AnimationInfo.ThresholdValue
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    AnimationInfo.Colors(0) = Color.FromName(.cboColor0.Text)
                    AnimationInfo.Colors(1) = Color.FromName(.cboColor1.Text)
                    AnimationInfo.Colors(2) = Color.FromName(.cboColor2.Text)
                    AnimationInfo.Colors(3) = Color.FromName(.cboColor3.Text)
                    AnimationInfo.Colors(4) = Color.FromName(.cboColor4.Text)
                    AnimationInfo.InvertColor = .chkInvert.Checked
                    AnimationInfo.ThresholdType = .cboThreshold.SelectedIndex
                    AnimationInfo.ThresholdValue = .txtThreshold.Text
                    RefreshDisplay()
                    IsModified = True
                End If
                .Dispose()
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    Private Sub mnuFileMovie_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileMovie.Click
        If Aggregate = enumAggregate.POR Then
            WarningMsg("You cannot create a movie unless the aggregation period is something other than Period of Record.")
            Exit Sub
        End If
        Dim aviMgr As AviFile.AviManager = Nothing
        Try
            Dim ThisLabel As String = Choose(DisplayMode() + 1, "ALL", "NUM", "MIN", "MAX", "AVG") & "_" & cboPCode.Text
            Dim MovieFile As String = ""
            With New SaveFileDialog
                .FileName = IO.Path.ChangeExtension(GISFileName, ".avi")
                .AddExtension = True
                .CheckPathExists = True
                .CreatePrompt = False
                .DefaultExt = ".avi"
                .Filter = "Movie File (*.avi)|*.avi"
                .FilterIndex = 0
                .OverwritePrompt = True
                .Title = "Create Animation Movie"
                .ValidateNames = True
                .FileName = String.Format("WRDBGIS_{0}.avi", ThisLabel)
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then MovieFile = .FileName
                .Dispose()
            End With

            If MovieFile = "" Then Exit Sub

            Try
                If My.Computer.FileSystem.FileExists(MovieFile) Then My.Computer.FileSystem.DeleteFile(MovieFile)
            Catch ex As Exception
                ErrorMsg("Unable to delete old video file; it might be open by another application.", ex)
                Exit Sub
            End Try

            With trkAnim

                .Value = .Minimum

                Try
                    aviMgr = New AviFile.AviManager(MovieFile, False)
                Catch ex As Exception
                    ErrorMsg("Unable to create video file; it might be open by another application.", ex)
                    Exit Sub
                End Try

                Dim aviStr As AviFile.VideoStream = Nothing

                Do
                    Application.DoEvents()
                    If .Value = .Minimum Then
                        Try
                            Dim framerate As Double = Choose(AnimationInfo.Speed, 2.4, 3, 4.8, 8, 12) 'determined empirically
                            aviStr = aviMgr.AddVideoStream(True, framerate, MapSnapshot)
                        Catch ex As Exception
                            ErrorMsg("Unable to create video file; it might be open by another application or the selected compression scheme may be unsupported on your computer.", ex)
                            Exit Sub
                        End Try
                    Else
                        aviStr.AddFrame(MapSnapshot)
                    End If
                    If IsCancelled Or .Value + .TickFrequency > .Maximum Then Exit Do
                    .Value += .TickFrequency
                Loop

                .Value = .Minimum
            End With

            Process.Start(MovieFile)
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If aviMgr IsNot Nothing Then aviMgr.Close()
        End Try
    End Sub

    Private Function MapSnapshot() As Bitmap
        Try
            'take snapshot of entire form

            Dim g1 As System.Drawing.Graphics = Me.CreateGraphics()
            Dim MyImage As New System.Drawing.Bitmap(Width, Height, g1)

            Dim g2 As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(MyImage)
            Dim dc1 As IntPtr = g1.GetHdc()
            Dim dc2 As IntPtr = g2.GetHdc()
            BitBlt(dc2, 0, 0, Me.ClientSize.Width, Me.ClientSize.Height, dc1, 0, 0, 13369376)
            g1.ReleaseHdc(dc1)
            g2.ReleaseHdc(dc2)

            'crop only the map and legend
            Return Crop(MyImage)
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Function Crop(ByVal bmForm As Bitmap) As Bitmap
        Try

            'copy part of form that has map and legend
            Dim r, w, h As Integer
            With picLegend
                r = .PointToScreen(New Point(.Width, .Height)).X
            End With
            With GISMap
                w = r - .PointToScreen(New Point(0, 0)).X
                'w = GISMap.Width + picLegend.Width
                h = .Height
            End With

            Dim bmMap As New Bitmap(w, h)

            Dim g2 As Graphics = Graphics.FromImage(bmMap)

            Dim pt As Point = GISMap.PointToScreen(New Point(0, 0))
            g2.DrawImage(bmForm, New Rectangle(0, 0, w, h), New Rectangle(Me.PointToClient(pt), New Size(w, h)), GraphicsUnit.Pixel)

            Dim fmt As New Drawing.StringFormat
            fmt.Alignment = StringAlignment.Near
            fmt.LineAlignment = StringAlignment.Near
            Dim s As String = String.Format("{0} of {1} aggregated by {2}: {3:" & MyDateFormat() & "}{4}", cboDisplay.Text, cboPCode.Text.Split(" ")(0), cboAggregate.Text, AnimationInfo.ThisDate, IIf(AnimationInfo.ThresholdType = enumThreshold.None, "", "; Color threshold=" & AnimationInfo.ThresholdValue))
            Dim ms As SizeF = g2.MeasureString(s, picLegend.Font, 0)
            g2.FillRectangle(Brushes.Black, 0, 0, ms.Width, ms.Height)
            g2.DrawString(s, picLegend.Font, Brushes.White, New Point(0, 0), fmt)

            Return bmMap

        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Function MapSnapshotold3() As Bitmap
        Try
            Dim g1 As System.Drawing.Graphics = GISMap.CreateGraphics()
            Dim MyImage As New System.Drawing.Bitmap(GISMap.ClientRectangle.Width, GISMap.ClientRectangle.Height, g1)

            'copy part of form that has map and legend
            Dim r, w, h As Integer
            With picLegend
                r = .PointToScreen(New Point(.Right, .Bottom)).X
            End With
            With GISMap
                w = r - .PointToScreen(New Point(0, 0)).X
                w = GISMap.Width + picLegend.Width
                h = .Height
            End With

            Dim bmMap As New Bitmap(w, h)

            Dim g2 As Graphics = Graphics.FromImage(bmMap)

            Dim dc1 As IntPtr = g1.GetHdc()
            Dim dc2 As IntPtr = g2.GetHdc()

            BitBlt(dc2, 0, 0, w, h, dc1, 0, 0, 13369376)
            g1.ReleaseHdc(dc1)
            g2.ReleaseHdc(dc2)
            Return MyImage
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Sub mnuViewLabels_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewLabels.Click, tbLabels.Click
        RefreshDisplay()
    End Sub

    Private Sub Progress(ByVal Value As Integer, ByVal MaxValue As Integer, ByVal ProgressText As String)
        WriteProgress(Value, MaxValue, ProgressText)
    End Sub
End Class

''' <summary>
''' This class exposes the MouseWheel event on the AxMap control
''' </summary>
Public Class MouseWheelMap
    Inherits AxMapWinGIS.AxMap

    Public Event MouseWheelDownEvent(ByVal sender As Object, ByVal e As MouseEventArgs)

    ' This is the event that should be intercepted to receive the mouse wheel event
    Public Event MouseWheelScrollEvent(ByVal sender As Object, ByVal e As MouseEventArgs)
    Public Event MouseWheelUpEvent(ByVal sender As Object, ByVal e As MouseEventArgs)
    Public Const MK_LBUTTON As Integer = &H1
    Public Const MK_MBUTTON As Integer = &H10
    Public Const MK_RBUTTON As Integer = &H2
    Public Const WM_MBUTTONDOWN As Integer = &H207
    Public Const WM_MBUTTONUP As Integer = &H208

    Public Const WM_MOUSEWHEEL As Integer = &H20A

    ' This method traps all windows messages, but only responds to the MouseWheel message
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_MOUSEWHEEL Or m.Msg = WM_MBUTTONUP Or m.Msg = WM_MBUTTONDOWN Then

            Dim xpos, ypos, mouseKeys, rotation, delta As Integer
            xpos = m.LParam.ToInt32() And 65535
            ypos = m.LParam.ToInt32() / 65536
            mouseKeys = m.WParam.ToInt32() And 65535
            rotation = m.WParam.ToInt32() / 65536
            delta = rotation / 120
            Dim mb As MouseButtons
            Select Case mouseKeys
                Case MK_LBUTTON : mb = Windows.Forms.MouseButtons.Left
                Case MK_RBUTTON : mb = Windows.Forms.MouseButtons.Right
                Case MK_MBUTTON : mb = Windows.Forms.MouseButtons.Middle
                Case Else : mb = Windows.Forms.MouseButtons.None
            End Select

            Select Case m.Msg
                Case WM_MOUSEWHEEL
                    RaiseEvent MouseWheelScrollEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
                Case WM_MBUTTONDOWN
                    RaiseEvent MouseWheelDownEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
                Case WM_MBUTTONUP
                    RaiseEvent MouseWheelUpEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
            End Select
            Exit Sub
        End If

        MyBase.WndProc(m)
    End Sub
End Class
